package com.np.xxpermissions1;

import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.app.Activity;
import android.app.ActivityManager;
import android.app.AlertDialog;
import android.app.Application;
import android.app.PendingIntent;
import android.app.ProgressDialog;
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.Context;
import android.content.ContextWrapper;
import android.content.DialogInterface;
import android.content.IntentFilter;
import android.content.res.AssetManager;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Point;
import android.graphics.PorterDuff;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.pdf.PdfRenderer;
import android.hardware.usb.UsbConstants;
import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbManager;
import android.media.AudioManager;
import android.media.MediaMetadataRetriever;
import android.media.MediaPlayer;
import android.net.TrafficStats;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Environment;
import android.os.FileUtils;
import android.os.Handler;
import android.os.LocaleList;
import android.os.ParcelFileDescriptor;
import android.os.PowerManager;
import android.os.Process;
import android.os.StatFs;
import android.os.StrictMode;
import android.os.storage.StorageManager;
import android.provider.DocumentsContract;
import android.provider.MediaStore;
import android.provider.OpenableColumns;
import android.text.method.ScrollingMovementMethod;
import android.util.DisplayMetrics;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.view.Window;
import android.view.WindowManager;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;
import android.widget.AdapterView;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.TextView;

import androidx.annotation.RequiresApi;
import androidx.annotation.StringRes;
import androidx.core.content.ContextCompat;
import androidx.core.graphics.drawable.DrawableCompat;
import androidx.core.view.ViewCompat;

import com.github.mjdev.libaums.UsbMassStorageDevice;
import com.github.mjdev.libaums.fs.UsbFile;
import com.github.mjdev.libaums.fs.UsbFileInputStream;
import com.github.mjdev.libaums.fs.UsbFileOutputStream;
import com.github.mjdev.libaums.partition.Partition;

import org.json.JSONArray;
import org.json.JSONException;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Method;
import java.nio.charset.StandardCharsets;
import java.text.DecimalFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.Executors;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 本项目用到的工具方法,包含工具如下:
 * ----Math2---------------------数学函数
 * ----Str-----------------------字符串操作
 * ----Disk----------------------磁盘空间
 * ----CPU-----------------------CPU位数
 * ----Intent--------------------Activity中的Intent操作
 * ----SoundPlayer---------------声音播放
 * ----SystemVolume--------------系统音量
 * ----Vibrator------------------振动
 * ----WakeLock------------------屏幕常亮
 * ----DateTime------------------时间工具
 * ----NetSpeed------------------实时网速
 * ----CalculationTime-----------计算耗时
 * ----Language------------------国际化语言(兼容Android7.0以下)
 * ----Display-------------------显示、分辨率等相关工具
 * ----UsbStoragePermission------U盘权限
 * ----UsbStorageReadWrite-------U盘读写
 * ----Base64--------------------Base64编解码
 * ----IO------------------------文件、文件夹操作相关
 * ----Uri-----------------------通过Uri获取文件路径
 * ----SharedPreferences---------数据库操作
 * ----Share---------------------文件分享
 * ----DialogMessage-------------消息对话框
 * ----DialogInput---------------输入对话框
 * ----DialogConfirm-------------确认对话框
 * ----DialogProgress------------进度对话框
 * ----ClickProxy----------------双击限制
 * ----Task----------------------异步任务封装
 * ----Thread--------------------异步线程封装
 * ----Image---------------------图像文件相关
 * --------Picture---------------图像文件
 * --------Movie-----------------视频文件
 * --------Pdf-------------------PDF文件
 * ----CircleBuffer--------------环形缓冲区
 * ----TextWriter----------------文本文件输出
 * ----IniFile-------------------INI文件操作类
 * ----DeviceInfo----------------设备信息
 */
public class Utils {

    /**
     * 数学函数
     */
    public static class Math2 {
        /**
         * 判断是否为整数
         */
        public static boolean isInteger(float value) {
            return ((int) Math.floor(value) == (int) Math.ceil(value));
        }

        /**
         * 获取整数部分
         */
        public static int integerPart(float value) {
            return (int) value;
        }

        /**
         * 获取小数部分
         */
        public static float fractionalPart(float value) {
            return value - (int) value;
        }

        /**
         * 判断是否能整除
         */
        public static boolean isDivisible(int value, int div) {
            return (value % div) == 0;
        }
    }

    /**
     * 字符串操作
     */
    public static class Str {
        /**
         * 主要用于校验字符串是否为空
         */
        public static boolean isEmpty(String string) {
            return (string == null) || string.trim().isEmpty();
        }

        /**
         * 格式化字符串
         */
        public static String format(String format, Object... args) {
            return String.format(Locale.US, format, args);
        }

        /**
         * 格式化字符串
         */
        public static String format(Context context, int idResource, Object... args) {
            return (idResource == 0) ? "" : String.format(Locale.US, context.getResources().getString(idResource), args);
        }

        /**
         * 格式化毫秒时间字符串
         */
        public static String formatTime(int ms, boolean showMs) {
            String ok;
            if (showMs) {
                int h = ((ms / 1000) / 60) / 60;
                int m = ((ms / 1000) / 60) % 60;
                int s = ((ms / 1000) % 60);
                int l = ((ms % 1000));
                if ((h == 0) && (m == 0) && (s == 0)) {
                    ok = String.format(Locale.US, "%dms", ms);
                } else if ((h == 0) && (m == 0)) {
                    ok = String.format(Locale.US, "%02d.%03d", s, l);
                } else if (h == 0) {
                    ok = String.format(Locale.US, "%02d:%02d.%03d", m, s, l);
                } else {
                    ok = String.format(Locale.US, "%d.%02d:%02d.%03d", h, m, s, l);
                }
            } else {
                ms = (int) (ms / 1000.0f + 0.5f);

                int h = (ms / 60) / 60;
                int m = (ms / 60) % 60;
                int s = (ms % 60);

                if (h == 0) {
                    ok = String.format(Locale.US, "%02d:%02d", m, s);
                } else {
                    ok = String.format(Locale.US, "%d:%02d:%02d", h, m, s);
                }
            }
            return ok;
        }

        /**
         * 格式化毫秒时间字符串
         */
        public static String formatTime(int msPos, int msMax, boolean showMs) {
            String ok;
            if (showMs) {
                int hPos = ((msPos / 1000) / 60) / 60;
                int mPos = ((msPos / 1000) / 60) % 60;
                int sPos = ((msPos / 1000) % 60);
                int lPos = ((msPos % 1000));

                int hMax = ((msMax / 1000) / 60) / 60;
                int mMax = ((msMax / 1000) / 60) % 60;
                int sMax = ((msMax / 1000) % 60);
                int lMax = ((msMax % 1000));

                if ((hMax == 0) && (mMax == 0) && (sMax == 0)) {
                    ok = String.format(Locale.US, "%d / %dms", lPos, lMax);
                } else if ((hMax == 0) && (mMax == 0)) {
                    ok = String.format(Locale.US, "%02d.%03d / %02d.%03d", sPos, lPos, sMax, lMax);
                } else if (hMax == 0) {
                    ok = String.format(Locale.US, "%02d:%02d.%03d / %02d:%02d.%03d", mPos, sPos, lPos, mMax, sMax, lMax);
                } else {
                    ok = String.format(Locale.US, "%d:%02d:%02d.%03d / %d:%02d:%02d.%03d", hPos, mPos, sPos, lPos, hMax, mMax, sMax, lMax);
                }
            } else {
                msPos = (int) (msPos / 1000.0f + 0.5f);
                msMax = (int) (msMax / 1000.0f + 0.5f);

                int hPos = (msPos / 60) / 60;
                int mPos = (msPos / 60) % 60;
                int sPos = (msPos % 60);

                int hMax = (msMax / 60) / 60;
                int mMax = (msMax / 60) % 60;
                int sMax = (msMax % 60);

                if (hMax == 0) {
                    ok = String.format(Locale.US, "%02d:%02d / %02d:%02d", mPos, sPos, mMax, sMax);
                } else {
                    ok = String.format(Locale.US, "%d:%02d:%02d / %d:%02d:%02d", hPos, mPos, sPos, hMax, mMax, sMax);
                }
            }
            return ok;
        }

        /**
         * 快速获取分割字符串并返回指定的子串
         */
        public static String getSplitString(String string, String regex, int sub) {
            String ok = string;
            if (!Str.isEmpty(string)) {
                String[] child = string.split(regex);
                if ((sub >= 0) && (sub < child.length)) {
                    ok = child[sub];
                }
            }
            return ok;
        }
    }

    /**
     * 磁盘空间
     */
    public static class Disk {

        /**
         * 获取SD卡的剩余容量(单位byte)
         */
        public static long getAvailable() {
            long ok = 0;
            try {
                // 检查是否存在SDCard
                if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) {
                    File file = Environment.getExternalStorageDirectory();
                    // Retrieve overall information about the space on a filesystem.
                    // This is a Wrapper for Unix statfs
                    StatFs statfs = new StatFs(file.getPath());
                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
                        ok = statfs.getAvailableBytes();
                    } else {
                        // 获取SDCard上每一个block的SIZE
                        long blockSize = statfs.getBlockSizeLong();
                        // 获取可供程序使用的Block的数量
                        long availableBlocks = statfs.getAvailableBlocksLong();
                        // 计算SDCard剩余大小Byte
                        ok = availableBlocks * blockSize;
                    }
                }
            } catch (Exception exception) {
                exception.printStackTrace();
            }
            return ok;
        }

        /**
         * 获取SD卡的总容量(单位byte)
         */
        public static long getTotal() {
            long ok = 0;
            try {
                // 检查是否存在SDCard
                if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) {
                    File file = Environment.getExternalStorageDirectory();
                    // Retrieve overall information about the space on a filesystem.
                    // This is a Wrapper for Unix statfs
                    StatFs statfs = new StatFs(file.getPath());
                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
                        ok = statfs.getTotalBytes();
                    } else {
                        // 获取SDCard上每一个block的SIZE
                        long blockSize = statfs.getBlockSizeLong();
                        // 获取总的Block的数量
                        long totalBlocks = statfs.getBlockCountLong();
                        // 计算SDCard剩余大小Byte
                        ok = totalBlocks * blockSize;
                    }
                }
            } catch (Exception exception) {
                exception.printStackTrace();
            }
            return ok;
        }

        /**
         * 获取SD卡的剩余容量百分比
         */
        public static float getAvailablePercent() {
            float ok = 0.0f;
            try {
                // 检查是否存在SDCard
                if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) {
                    File file = Environment.getExternalStorageDirectory();
                    // Retrieve overall information about the space on a filesystem.
                    // This is a Wrapper for Unix statfs
                    StatFs statfs = new StatFs(file.getPath());
                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
                        ok = 100.0f * statfs.getAvailableBytes() / statfs.getTotalBytes();
                    } else {
                        // 获取总的Block的数量
                        long totalBlocks = statfs.getBlockCountLong();
                        // 获取可供程序使用的Block的数量
                        long availableBlocks = statfs.getAvailableBlocksLong();
                        // 计算SDCard剩余大小Byte
                        ok = 100.0f * availableBlocks / totalBlocks;
                    }
                }
            } catch (Exception exception) {
                exception.printStackTrace();
            }
            return ok;
        }

        /**
         * 获取SD卡的剩余容量
         */
        public static String getAvailableString() {
            return Disk.toString(Disk.getAvailable(), "#.00");
        }

        /**
         * 转换文件大小为字符串
         */
        public static String toString(long size, String pattern) {
            String ok = "0B";
            if (size > 0) {
                DecimalFormat df = new DecimalFormat(pattern);
                if (size < 1024) {
                    ok = df.format((double) size) + "B";
                } else if (size < 1048576) {
                    ok = df.format((double) size / 1024) + "KB";
                } else if (size < 1073741824) {
                    ok = df.format((double) size / 1048576) + "MB";
                } else {
                    ok = df.format((double) size / 1073741824) + "GB";
                }
            }
            return ok;
        }
    }

    /**
     * CPU位数
     */
    public static class CPU {
        /**
         * 判断系统是否为64位
         */
        public static boolean is64() {
            boolean result = false;
            {
                try {
                    String processor = CPU.getFieldFromCPU("Processor");
                    if (processor != null) {
                        // mProcessor = AArch64 Processor rev 4 (aarch64)
                        if (processor.contains("aarch64")) {
                            result = true;
                        }
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            return result;
        }

        /**
         * 获取当前进程名字
         */
        public static String getCurrentProcess(Context context) {
            int pid = Process.myPid();
            String processName = "";
            ActivityManager manager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
            for (ActivityManager.RunningAppProcessInfo process : manager.getRunningAppProcesses()) {
                if (process.pid == pid) {
                    processName = process.processName;
                }
            }
            return processName;
        }

        /* cat/proc/cpuinfo
           processor       : 0
           Processor       : AArch64 Processor rev 4 (aarch64)
           model name      : AArch64 Processor rev 4 (aarch64)
           BogoMIPS        : 26.00
           Features        : fp asimd evtstrm aes pmull sha1 sha2 crc32
           CPU implementer : 0x41
           CPU architecture: 8
           CPU variant     : 0x0
           CPU part        : 0xd03
           CPU revision    : 4
        */
        private static String getFieldFromCPU(String field) throws IOException {
            try (BufferedReader br = new BufferedReader(new FileReader("/proc/cpuinfo"))) {
                Pattern p = Pattern.compile(field + "\\s*:\\s*(.*)");
                String line;
                while ((line = br.readLine()) != null) {
                    Matcher m = p.matcher(line);
                    if (m.matches()) return m.group(1);
                }
            }
            return null;
        }
    }

    /**
     * Activity中的Intent操作
     */
    public static class Intent {
        /**
         * 从Intent中获取值
         */
        public static boolean getValue(Activity activity, android.content.Intent intent, String key, boolean defValue) {
            boolean ok = defValue;
            if ((activity != null) && (intent != null)) {
                ok = intent.getBooleanExtra(key, defValue);
            }
            return ok;
        }

        /**
         * 从Intent中获取值
         */
        public static boolean[] getArrayBoolean(Activity activity, android.content.Intent intent, String key) {
            boolean[] ok = null;
            if ((activity != null) && (intent != null)) {
                ok = intent.getBooleanArrayExtra(key);
            }
            return ok;
        }

        /**
         * 从Intent中获取值
         */
        public static byte getValue(Activity activity, android.content.Intent intent, String key, byte defValue) {
            byte ok = defValue;
            if ((activity != null) && (intent != null)) {
                ok = intent.getByteExtra(key, defValue);
            }
            return ok;
        }

        /**
         * 从Intent中获取值
         */
        public static byte[] getArrayByte(Activity activity, android.content.Intent intent, String key) {
            byte[] ok = null;
            if ((activity != null) && (intent != null)) {
                ok = intent.getByteArrayExtra(key);
            }
            return ok;
        }

        /**
         * 从Intent中获取值
         */
        public static char getValue(Activity activity, android.content.Intent intent, String key, char defValue) {
            char ok = defValue;
            if ((activity != null) && (intent != null)) {
                ok = intent.getCharExtra(key, defValue);
            }
            return ok;
        }

        /**
         * 从Intent中获取值
         */
        public static char[] getArrayChar(Activity activity, android.content.Intent intent, String key) {
            char[] ok = null;
            if ((activity != null) && (intent != null)) {
                ok = intent.getCharArrayExtra(key);
            }
            return ok;
        }

        /**
         * 从Intent中获取值
         */
        public static int getValue(Activity activity, android.content.Intent intent, String key, int defValue) {
            int ok = defValue;
            if ((activity != null) && (intent != null)) {
                ok = intent.getIntExtra(key, defValue);
            }
            return ok;
        }

        /**
         * 从Intent中获取值
         */
        public static int[] getArrayInt(Activity activity, android.content.Intent intent, String key) {
            int[] ok = null;
            if ((activity != null) && (intent != null)) {
                ok = intent.getIntArrayExtra(key);
            }
            return ok;
        }

        /**
         * 从Intent中获取值
         */
        public static long getValue(Activity activity, android.content.Intent intent, String key, long defValue) {
            long ok = defValue;
            if ((activity != null) && (intent != null)) {
                ok = intent.getLongExtra(key, defValue);
            }
            return ok;
        }

        /**
         * 从Intent中获取值
         */
        public static long[] getArrayLong(Activity activity, android.content.Intent intent, String key) {
            long[] ok = null;
            if ((activity != null) && (intent != null)) {
                ok = intent.getLongArrayExtra(key);
            }
            return ok;
        }

        /**
         * 从Intent中获取值
         */
        public static float getValue(Activity activity, android.content.Intent intent, String key, float defValue) {
            float ok = defValue;
            if ((activity != null) && (intent != null)) {
                ok = intent.getFloatExtra(key, defValue);
            }
            return ok;
        }

        /**
         * 从Intent中获取值
         */
        public static float[] getArrayFloat(Activity activity, android.content.Intent intent, String key) {
            float[] ok = null;
            if ((activity != null) && (intent != null)) {
                ok = intent.getFloatArrayExtra(key);
            }
            return ok;
        }

        /**
         * 从Intent中获取值
         */
        public static double getValue(Activity activity, android.content.Intent intent, String key, double defValue) {
            double ok = defValue;
            if ((activity != null) && (intent != null)) {
                ok = intent.getDoubleExtra(key, defValue);
            }
            return ok;
        }

        /**
         * 从Intent中获取值
         */
        public static double[] getArrayDouble(Activity activity, android.content.Intent intent, String key) {
            double[] ok = null;
            if ((activity != null) && (intent != null)) {
                ok = intent.getDoubleArrayExtra(key);
            }
            return ok;
        }

        /**
         * 从Intent中获取值
         */
        public static String getValue(Activity activity, android.content.Intent intent, String key, String defValue) {
            String ok = defValue;
            if ((activity != null) && (intent != null)) {
                ok = intent.getStringExtra(key);
            }
            return ok;
        }

        /**
         * 从Intent中获取值
         */
        public static String[] getArrayString(Activity activity, android.content.Intent intent, String key) {
            String[] ok = null;
            if ((activity != null) && (intent != null)) {
                ok = intent.getStringArrayExtra(key);
            }
            return ok;
        }
    }

    /**
     * 声音播放
     */
    public static class SoundPlayer {

        public static class Builder {
            static SoundPlayer soundPlayer;
            static Builder instance = new Builder();

            public static Builder newInstance(Context context, int resource) {
                soundPlayer = new SoundPlayer(context, resource);
                return instance;
            }

            public void start() {
                soundPlayer.m_player.start();
            }
        }

        private final MediaPlayer m_player;

        private SoundPlayer(Context context, int resource) {
            m_player = MediaPlayer.create(context, resource);
            m_player.setLooping(false);
        }
    }

    /**
     * 声音播放
     */
    public static class SystemVolume {
        private final AudioManager m_audioManager;

        // 获取系统的Audio管理者
        public SystemVolume(Context context) {
            m_audioManager = (AudioManager) context.getSystemService(Service.AUDIO_SERVICE);
        }

        // 获取最大多媒体音量
        public int getMediaMaxVolume() {
            return m_audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
        }

        // 获取当前多媒体音量
        public int getMediaVolume() {
            return m_audioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
        }

        // 获取最大通话音量
        public int getCallMaxVolume() {
            return m_audioManager.getStreamMaxVolume(AudioManager.STREAM_VOICE_CALL);
        }

        // 获取当前通话音量
        public int getCallVolume() {
            return m_audioManager.getStreamVolume(AudioManager.STREAM_VOICE_CALL);
        }

        // 获取最大系统音量
        public int getSystemMaxVolume() {
            return m_audioManager.getStreamMaxVolume(AudioManager.STREAM_SYSTEM);
        }

        // 获取当前系统音量
        public int getSystemVolume() {
            return m_audioManager.getStreamVolume(AudioManager.STREAM_SYSTEM);
        }

        // 获取最大提示音量
        public int getAlermMaxVolume() {
            return m_audioManager.getStreamMaxVolume(AudioManager.STREAM_ALARM);
        }

        // 获取当前提示音量
        public int getAlermVolume() {
            return m_audioManager.getStreamVolume(AudioManager.STREAM_ALARM);
        }

        // 设置多媒体音量
        public void setMediaVolume(int volume) {
            m_audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, volume, AudioManager.FLAG_PLAY_SOUND | AudioManager.FLAG_SHOW_UI);
        }

        // 设置通话音量
        public void setCallVolume(int volume) {
            m_audioManager.setStreamVolume(AudioManager.STREAM_VOICE_CALL, volume, AudioManager.FLAG_PLAY_SOUND | AudioManager.FLAG_SHOW_UI);
        }

        // 设置提示音量
        public void setAlermVolume(int volume) {
            m_audioManager.setStreamVolume(AudioManager.STREAM_ALARM, volume, AudioManager.FLAG_PLAY_SOUND | AudioManager.FLAG_SHOW_UI);
        }

        // 关闭/打开扬声器播放
        public void setSpeakerStatus(boolean on) {
            if (on) { //扬声器
                m_audioManager.setSpeakerphoneOn(true);
                m_audioManager.setMode(AudioManager.MODE_NORMAL);
            } else {
                // 设置最大音量
                int max = m_audioManager.getStreamMaxVolume(AudioManager.STREAM_VOICE_CALL);
                m_audioManager.setStreamVolume(AudioManager.STREAM_VOICE_CALL, max, AudioManager.STREAM_VOICE_CALL);
                // 设置成听筒模式
                m_audioManager.setMode(AudioManager.MODE_IN_COMMUNICATION);
                m_audioManager.setSpeakerphoneOn(false);// 关闭扬声器
                m_audioManager.setRouting(AudioManager.MODE_NORMAL, AudioManager.ROUTE_EARPIECE, AudioManager.ROUTE_ALL);
            }
        }
    }

    /**
     * 振动
     */
    public static class Vibrator {
        /**
         * 使用振动
         */
        public static void setVibrator(Context context, long milliseconds) {
            android.os.Vibrator vibrator = (android.os.Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
            if (vibrator != null) vibrator.vibrate(milliseconds);
        }
    }

    /**
     * 屏幕常亮
     */
    public static class WakeLock {
        private static PowerManager.WakeLock m_wakeLock = null;

        @SuppressLint({"InvalidWakeLockTag", "WakelockTimeout"})
        public static void lock(Context context, boolean lock) {
            if (lock) {
                if (m_wakeLock == null) {
                    PowerManager powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
                    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
                        m_wakeLock = powerManager.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK, "WakeLock");
                    } else {
                        m_wakeLock = powerManager.newWakeLock(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON, "WakeLock");
                    }
                    if (m_wakeLock != null) m_wakeLock.setReferenceCounted(false);
                }
                if (m_wakeLock != null) m_wakeLock.acquire();
            } else {
                WakeLock.unlock();
            }
        }

        public static void unlock() {
            if (m_wakeLock != null) {
                m_wakeLock.release();
                m_wakeLock = null;
            }
        }
    }

    /**
     * 时间封装
     */
    public static class DateTime {

        /**
         * 创建一个日期
         */
        public static Calendar makeCalendar(int year, int month, int date) {
            Calendar ok = Calendar.getInstance();
            ok.set(year, month, date);
            return ok;
        }

        /**
         * 创建一个日期
         */
        public static Calendar makeCalendar(int year, int month, int date, int hourOfDay, int minute, int second) {
            Calendar ok = Calendar.getInstance();
            ok.set(year, month, date, hourOfDay, minute, second);
            return ok;
        }

        /**
         * 转换当前日期-时间为字符串
         */
        public static String currentDateTime(String format) {
            return dateTime(System.currentTimeMillis(), format);
        }

        /**
         * 转换当前日期-时间为字符串
         */
        public static String currentDateTime(boolean chinese) {
            return dateTime(System.currentTimeMillis(), chinese);
        }

        /**
         * 转换指定日期-时间为字符串
         */
        public static String dateTime(long value, String format) {
            return new SimpleDateFormat(format, Locale.US).format(new Date(value));
        }

        /**
         * 转换指定日期-时间为字符串
         */
        public static String dateTime(long value, boolean chinese) {
            return DateTime.dateTime(value, chinese ? "yyyy/MM/dd HH:mm:ss" : "MMM d, yyyy HH:mm:ss");
        }

        /**
         * 转换字符串为日期-时间值
         */
        public static long dateTime(String value, String format) {
            long ok = 0;
            try {
                Date date = new SimpleDateFormat(format, Locale.US).parse(value);
                if (date != null) ok = date.getTime();
            } catch (ParseException e) {
                e.printStackTrace();
            }
            return ok;
        }

        /**
         * 转换字符串为日期-时间值
         */
        public static long dateTime(String value, boolean chinese) {
            return DateTime.dateTime(value, chinese ? "yyyy/MM/dd HH:mm:ss" : "MMM d, yyyy HH:mm:ss");
        }

        /**
         * 转换当前日期为字符串
         */
        public static String currentDate(String format) {
            return date(System.currentTimeMillis(), format);
        }

        /**
         * 转换当前日期为字符串
         */
        public static String currentDate(boolean chinese) {
            return date(System.currentTimeMillis(), chinese);
        }

        /**
         * 转换指定日期为字符串
         */
        public static String date(long value, String format) {
            return new SimpleDateFormat(format, Locale.US).format(new Date(value));
        }

        /**
         * 转换指定日期为字符串
         */
        public static String date(long value, boolean chinese) {
            return DateTime.date(value, chinese ? "yyyy/MM/dd" : "MMM d, yyyy");
        }

        /**
         * 转换字符串为日期值
         */
        public static long date(String value, String format) {
            long ok = 0;
            try {
                Date date = new SimpleDateFormat(format, Locale.US).parse(value);
                if (date != null) ok = date.getTime();
            } catch (ParseException e) {
                e.printStackTrace();
            }
            return ok;
        }

        /**
         * 转换字符串为日期值
         */
        public static long date(String value, boolean chinese) {
            return DateTime.date(value, chinese ? "yyyy/MM/dd" : "MMM d, yyyy");
        }

        /**
         * 转换当前时间为字符串
         */
        public static String currentTime() {
            return DateTime.currentTime("HH:mm:ss");
        }

        /**
         * 转换当前时间为字符串
         */
        public static String currentTime(String format) {
            return new SimpleDateFormat(format, Locale.US).format(new Date(System.currentTimeMillis()));
        }
    }

    /**
     * 实时网速
     */
    public static class NetSpeed {
        private static long mLastTotalRxBytes = 0;
        private static long mLastTimeStamp = 0;

        public static String getSpeed(Context context) {
            String ok;
            {
                long nowTotalRxBytes = getTotalRxBytes(context.getApplicationInfo().uid);
                long nowTimeStamp = System.currentTimeMillis();
                // 毫秒转换
                long speed = (nowTotalRxBytes - mLastTotalRxBytes) * 1000 / (nowTimeStamp - mLastTimeStamp);
                mLastTimeStamp = nowTimeStamp;
                mLastTotalRxBytes = nowTotalRxBytes;
                if (speed < 1024)
                    ok = Str.format("%dB/s", speed);
                else if (speed < 1024 * 1024)
                    ok = Str.format("%.1fK/s", speed / 1024.0);
                else
                    ok = Str.format("%.1fM/s", speed / 1024.0 / 1024.0);
            }
            return ok;
        }

        private static long getTotalRxBytes(int uid) {
            return (TrafficStats.getUidRxBytes(uid) == TrafficStats.UNSUPPORTED) ? 0 : TrafficStats.getTotalRxBytes();
        }
    }

    /**
     * 计算耗时
     */
    public static class CalculationTime {

        private static final Map<String, Long> mMap = new HashMap<>();

        /**
         * 查询关键字为key的耗时
         */
        public static String query(String key) {
            long subTime = 0;
            long current = System.currentTimeMillis();
            if (mMap.containsKey(key)) subTime = current - mMap.get(key);
            mMap.put(key, current);
            return key + "=" + subTime + "ms";
        }
    }

    /**
     * 国际化语言
     */
    public static class Language {
        // 保存本次运行过程中使用的当前语言
        private static Locale m_current = null;

        /**
         * 模块(app)退出时复位,以便重启时重置
         */
        public static void reset() {
            m_current = null;
        }

        /**
         * 重载Activity.attachBaseContext并调用
         */
        public static ContextWrapper attachBaseContext(Context context, Locale locale) {
            m_current = locale;
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
                return new ContextWrapper(Language.createConfigurationResources(context, locale));
            } else {
                return new ContextWrapper(Language.setConfiguration(context, locale));
            }
        }

        /**
         * 重载Activity.attachBaseContext并调用
         */
        public static ContextWrapper attachBaseContext(Context context, Locale[] supports, Locale def) {
            return Language.attachBaseContext(context, Arrays.asList(supports), def);
        }

        /**
         * 重载Activity.attachBaseContext并调用
         */
        public static ContextWrapper attachBaseContext(Context context, List<Locale> supports, Locale def) {
            if (m_current == null) {
                // 从context中获取语言,若此语言在支持列表中(相同或相似),则直接设置,否则使用默认语言
                m_current = Language.getContextLocale(context, supports, def);
            }
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
                return new ContextWrapper(Language.createConfigurationResources(context, m_current));
            } else {
                return new ContextWrapper(Language.setConfiguration(context, m_current));
            }
        }

        /**
         * 更改语言
         */
        public static void changeLanguage(Context context, Locale locale) {
            Resources resources = context.getResources();
            Configuration configuration = resources.getConfiguration();
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
                configuration.setLocale(locale);
                configuration.setLocales(new LocaleList(locale));
                context.createConfigurationContext(configuration);
            } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
                configuration.setLocale(locale);
            } else {
                configuration.locale = locale;
            }
            resources.updateConfiguration(configuration, resources.getDisplayMetrics());
        }

        /**
         * 获取本地应用的实际的多语言信息
         */
        public static Locale getContextLocale(Context context) {
            Locale locale;
            {
                // 获取应用语言
                Resources resources = context.getResources();
                Configuration configuration = resources.getConfiguration();
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
                    locale = configuration.getLocales().get(0);
                } else {
                    locale = configuration.locale;
                }
            }
            return locale;
        }

        /**
         * 判断本地应用语言
         */
        public static boolean isStartWith(Context context, String key) {
            return Language.getContextLocale(context).toString().startsWith(key);
        }

        private static Context setConfiguration(Context context, Locale locale) {
            Resources resources = context.getResources();
            Configuration configuration = resources.getConfiguration();
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
                configuration.setLocale(locale);
            } else {
                configuration.locale = locale;
            }
            resources.updateConfiguration(configuration, resources.getDisplayMetrics());
            return context;
        }

        @TargetApi(Build.VERSION_CODES.N)
        private static Context createConfigurationResources(Context context, Locale locale) {
            Resources resources = context.getResources();
            Configuration configuration = resources.getConfiguration();
            configuration.setLocale(locale);
            configuration.setLocales(new LocaleList(locale));
            return context.createConfigurationContext(configuration);
        }

        // 获取最佳匹配的语言
        private static Locale getContextLocale(Context context, List<Locale> supports, Locale def) {
            Locale ok = null;
            {
                Locale contextLocale = Language.getContextLocale(context);

                // 从支持列表中查找相同的语言
                if (supports != null) {
                    for (Locale locale : supports) {
                        if (contextLocale.equals(locale)) {
                            ok = locale;
                            break;
                        }
                    }
                }

                // 从支持列表中查找相似的语言
                if (ok == null) {
                    if (supports != null) {
                        for (Locale locale : supports) {
                            if (contextLocale.getLanguage().equals(locale.getLanguage())) {
                                if (!contextLocale.getCountry().equals(locale.getCountry())) {
                                    ok = new Locale(contextLocale.getLanguage());
                                } else {
                                    ok = new Locale(contextLocale.getLanguage(), contextLocale.getCountry());
                                    break;
                                }
                            }
                        }
                    }
                }

                if (ok == null) ok = def;
            }
            return ok;
        }
    }

    /**
     * U盘权限
     */
    public static class UsbStoragePermission {
        private static final String USB_DEVICE_PERMISSION = "com.android.usb.USB_PERMISSION";

        private final Context m_context;
        private final UsbManager m_usbManager;
        private UsbDevice m_usbDevice;
        private OnListener m_listener;

        public interface OnListener {
            void onGrantedPermission();
        }

        public UsbStoragePermission(Context context) {
            m_context = context.getApplicationContext();
            m_usbManager = (UsbManager) context.getSystemService(Context.USB_SERVICE);
            IntentFilter filter = new IntentFilter(USB_DEVICE_PERMISSION);
            m_context.registerReceiver(new BroadcastReceiver() {
                public void onReceive(Context context, android.content.Intent intent) {
                    String action = intent.getAction();
                    if (USB_DEVICE_PERMISSION.equals(action)) {
                        synchronized (this) {
                            UsbDevice usbDevice = (UsbDevice) intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
                            if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
                                if (null != usbDevice) {
                                    if (m_listener != null) m_listener.onGrantedPermission();
                                }
                            }
                        }
                    }
                }
            }, filter);
        }

        /**
         * 是否存在U盘
         */
        public boolean exist() {
            boolean ok = false;
            if (m_usbManager != null) {
                HashMap<String, UsbDevice> map = m_usbManager.getDeviceList();
                for (Map.Entry<String, UsbDevice> entry : map.entrySet()) {
                    UsbDevice usbDevice = entry.getValue();
                    if (usbDevice.getInterface(0).getInterfaceClass() == UsbConstants.USB_CLASS_MASS_STORAGE) {
                        m_usbDevice = usbDevice;
                        ok = true;
                        break;
                    }
                }
            }
            return ok;
        }

        /**
         * 该U盘是否支持读写
         */
        public boolean isSupported() {
            boolean ok = false;
            UsbMassStorageDevice[] devices = UsbMassStorageDevice.getMassStorageDevices(m_context);
            if (devices.length != 0) {
                try {
                    devices[0].init();
                    List<Partition> list = devices[0].getPartitions();
                    ok = (list.size() != 0);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            return ok;
        }

        /**
         * 是否拥有权限
         */
        public boolean hasPermission() {
            return (m_usbDevice != null) && m_usbManager.hasPermission(m_usbDevice);
        }

        /**
         * 申请权限
         */
        public void requestPermission(OnListener listener) {
            if (m_usbDevice != null) {
                if (!m_usbManager.hasPermission(m_usbDevice)) {
                    m_listener = listener;
                    if (Build.VERSION.SDK_INT >= 31) {   // 31=android.os.Build.VERSION_CODES.S
                        m_usbManager.requestPermission(m_usbDevice, PendingIntent.getBroadcast(m_context, 0, new android.content.Intent(USB_DEVICE_PERMISSION), PendingIntent.FLAG_IMMUTABLE)); // Android 12要求必须添加FLAG_IMMUTABLE标志
                    } else {
                        m_usbManager.requestPermission(m_usbDevice, PendingIntent.getBroadcast(m_context, 0, new android.content.Intent(USB_DEVICE_PERMISSION), 0));
                    }
                }
            }
        }
    }

    /**
     * U盘读写
     */
    public static class UsbStorageReadWrite {
        private UsbMassStorageDevice m_storageDevice = null;
        private UsbFile m_rootPath = null;

        public UsbStorageReadWrite(Context context) {
            UsbMassStorageDevice[] storageDevices = UsbMassStorageDevice.getMassStorageDevices(context);
            if (storageDevices.length > 0) {
                m_storageDevice = storageDevices[0];
                try {
                    m_storageDevice.init();
                    List<Partition> list = m_storageDevice.getPartitions();
                    m_rootPath = list.isEmpty() ? null : list.get(0).getFileSystem().getRootDirectory();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

        @Override
        protected void finalize() throws Throwable {
            this.close();
            super.finalize();
        }

        /**
         * 关闭U盘读写
         */
        public void close() {
            if (m_storageDevice != null) {
                m_storageDevice.close();
                m_storageDevice = null;
                m_rootPath = null;
            }
        }

        /**
         * U盘是否已经打开
         */
        public boolean isOpened() {
            return (m_rootPath != null);
        }

        /**
         * 并返回根目录
         */
        public UsbFile getRootPath() {
            return m_rootPath;
        }

        /**
         * 打开U盘中指定的目录/文件(路径必须以'/'分隔)
         */
        public UsbFile open(String path) {
            return this.open(m_rootPath, path);
        }

        /**
         * 打开U盘中指定的目录/文件(路径必须以'/'分隔)
         */
        public UsbFile open(UsbFile parent, String path) {
            UsbFile ok = null;
            if ((parent != null) && !Str.isEmpty(path)) {
                try {
                    ok = parent.search(path);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            return ok;
        }

        /**
         * 在U盘中创建目录(路径必须以'/'分隔)
         */
        public UsbFile createPath(String path) {
            return this.createPath(m_rootPath, path);
        }

        /**
         * 在U盘中创建目录(路径必须以'/'分隔)
         */
        public UsbFile createPath(UsbFile parent, String path) {
            UsbFile ok = null;
            if ((parent != null) && !Str.isEmpty(path)) {
                String[] splits = path.split("/");
                if (splits.length > 0) {
                    ok = parent;
                    for (String item : splits) {
                        try {
                            UsbFile search = ok.search(item);
                            ok = (search != null) ? search : ok.createDirectory(item);
                        } catch (IOException e) {
                            e.printStackTrace();
                            ok = null;
                            break;
                        }
                    }
                }
            }
            return ok;
        }

        /**
         * 在U盘中创建目录(路径必须以'/'分隔)
         */
        public UsbFile createFile(String path) {
            return this.createFile(m_rootPath, path);
        }

        /**
         * 在U盘中创建文件(路径必须以'/'分隔)
         */
        public UsbFile createFile(UsbFile parent, String path) {
            UsbFile ok = null;
            if ((parent != null) && !Str.isEmpty(path)) {
                String[] splits = path.split("/");
                if (splits.length > 0) {
                    ok = parent;
                    if (splits.length > 1) {
                        StringBuilder front = new StringBuilder();
                        for (int n = 0; n < splits.length - 1; ++n) {
                            front.append(splits[n]);
                        }
                        ok = this.createPath(parent, front.toString());
                    }
                    if (ok != null) {
                        try {
                            String name = splits[splits.length - 1];
                            UsbFile search = ok.search(name);
                            ok = (search != null) ? search : ok.createFile(name);
                        } catch (IOException e) {
                            e.printStackTrace();
                            ok = null;
                        }
                    }
                }
            }
            return ok;
        }

        /**
         * 删除U盘中指定的目录/文件(路径必须以'/'分隔)
         */
        public boolean delete(String path) {
            return this.delete(m_rootPath, path);
        }

        /**
         * 删除U盘中指定的目录/文件(路径必须以'/'分隔)
         */
        public boolean delete(UsbFile parent, String path) {
            boolean ok = false;
            if ((parent != null) && !Str.isEmpty(path)) {
                UsbFile file = this.open(parent, path);
                if (file != null) {
                    try {
                        file.delete();
                        ok = true;
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
            return ok;
        }

        /**
         * 向U盘复制文件或文件夹(路径必须以'/'分隔)
         */
        public boolean copyToU(String src, String dst) {
            return this.copyToU(new File(src), this.open(dst));
        }

        /**
         * 向U盘复制文件或文件夹(路径必须以'/'分隔)
         */
        public boolean copyToU(File src, String dst) {
            return this.copyToU(src, this.open(dst));
        }

        /**
         * 向U盘复制文件或文件夹(路径必须以'/'分隔)
         */
        public boolean copyToU(String src, UsbFile dst) {
            return this.copyToU(new File(src), dst);
        }

        /**
         * 向U盘复制文件或文件夹(路径必须以'/'分隔)
         */
        public boolean copyToU(File src, UsbFile dst) {
            boolean ok = false;
            if (src != null && dst != null) {
                File[] list = src.listFiles();
                if (list != null) {
                    for (File file : list) {
                        try {
                            if (file.isDirectory()) {
                                if (!(ok = this.copyToU(file, dst.createDirectory(file.getName()))))
                                    break;
                            } else if (file.isFile()) {
                                if (!(ok = this._copyToU_(file, dst)))
                                    break;
                            }
                        } catch (IOException e) {
                            e.printStackTrace();
                            break;
                        }
                    }
                }
            }
            return ok;
        }

        /**
         * 从U盘复制文件或文件夹(路径必须以'/'分隔)
         */
        public boolean copyFromU(String src, String dst) {
            return this.copyFromU(this.open(src), new File(dst));
        }

        /**
         * 从U盘复制文件或文件夹(路径必须以'/'分隔)
         */
        public boolean copyFromU(String src, File dst) {
            return this.copyFromU(this.open(src), dst);
        }

        /**
         * 从U盘复制文件或文件夹(路径必须以'/'分隔)
         */
        public boolean copyFromU(UsbFile src, String dst) {
            return this.copyFromU(src, new File(dst));
        }

        /**
         * 从U盘复制文件或文件夹(路径必须以'/'分隔)
         */
        public boolean copyFromU(UsbFile src, File dst) {
            boolean ok = false;
            if (src != null && dst != null) {
                if (dst.exists() || dst.mkdirs()) {
                    try {
                        UsbFile[] list = src.listFiles();
                        if (list != null) {
                            for (UsbFile file : list) {
                                if (file.isDirectory()) {
                                    if (!(ok = this.copyFromU(file, new File(dst, file.getName()))))
                                        break;
                                } else {
                                    if (!(ok = this._copyFromU_(file, dst)))
                                        break;
                                }
                            }
                        }
                    } catch (IOException e) {
                        e.printStackTrace();
                        ok = false;
                    }
                }
            }
            return ok;
        }

        private boolean _copyToU_(File file, UsbFile usbFile) {
            boolean ok = false;
            if (file != null && usbFile != null) {
                try {
                    BufferedInputStream from = new BufferedInputStream(new FileInputStream(file));
                    BufferedOutputStream to = new BufferedOutputStream(new UsbFileOutputStream(usbFile.createFile(file.getName())));
                    byte[] bt = new byte[65536];
                    int read;
                    while ((read = from.read(bt)) > 0) {
                        to.write(bt, 0, read);
                    }
                    to.flush();
                    to.close();
                    from.close();
                    ok = true;
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            return ok;
        }

        private boolean _copyFromU_(UsbFile usbFile, File file) {
            boolean ok = false;
            if (usbFile != null && file != null) {
                try {
                    BufferedInputStream from = new BufferedInputStream(new UsbFileInputStream(usbFile));
                    BufferedOutputStream to = new BufferedOutputStream(new FileOutputStream(new File(file, usbFile.getName())));
                    byte[] bt = new byte[65536];
                    int read;
                    while ((read = from.read(bt)) > 0) {
                        to.write(bt, 0, read);
                    }
                    to.flush();
                    to.close();
                    from.close();
                    ok = true;
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            return ok;
        }
    }

    /**
     * 显示相关功能的封装
     */
    public static class Display {

        // 转换单位
        public static final int COMPLEX_UNIT_PX = 0;
        public static final int COMPLEX_UNIT_DP = 1;
        public static final int COMPLEX_UNIT_SP = 2;
        public static final int COMPLEX_UNIT_PT = 3;
        public static final int COMPLEX_UNIT_IN = 4;
        public static final int COMPLEX_UNIT_MM = 5;

        /**
         * 全屏下显示/隐藏导航栏和状态栏
         */
        public static void setNavigationBarAndStatusBar(Activity activity, boolean showNavigationBar, boolean showStatusBar) {
            if (activity != null) {
                View decorView = activity.getWindow().getDecorView();
                if ((Build.VERSION.SDK_INT > Build.VERSION_CODES.HONEYCOMB) && (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT)) {
                    if (showNavigationBar) {
                        decorView.setSystemUiVisibility(View.VISIBLE);
                    } else {
                        decorView.setSystemUiVisibility(View.GONE);
                    }
                } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
                    if (showNavigationBar) {
                        decorView.setSystemUiVisibility(View.SCREEN_STATE_OFF);
                    } else {
                        int options = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY | (showStatusBar ? 0 : View.SYSTEM_UI_FLAG_FULLSCREEN);
                        decorView.setSystemUiVisibility(options);
                        decorView.setOnSystemUiVisibilityChangeListener(flag -> {
                            if ((flag & View.SYSTEM_UI_FLAG_FULLSCREEN) == 0)
                                decorView.setSystemUiVisibility(options);
                        });
                    }
                }
            }
        }

        /**
         * 全屏下显示/隐藏状态栏和导航栏
         */
        public static void setNavigationBarAndStatusBar(Window window, boolean hide) {
            if ((window != null) && hide) {
                window.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));

                window.setFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE);
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
                    int options = View.SYSTEM_UI_FLAG_LAYOUT_STABLE
                            | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
                            | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
                            | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
                            | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                            | View.SYSTEM_UI_FLAG_FULLSCREEN;
                    window.getDecorView().setSystemUiVisibility(options);
                }
                window.clearFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE);
            }
        }

        /**
         * 全屏下显示/隐藏状态栏和导航栏
         */
        public static void setNavigationBarAndStatusBar(AlertDialog dialog, boolean hide) {
            if (dialog != null) {
                if (!hide) {
                    dialog.show();
                } else {
                    Window window = dialog.getWindow();
                    if (window != null) {
                        // 控制状态栏和导航栏
                        window.setFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE);

                        dialog.show();

                        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
                            int options = View.SYSTEM_UI_FLAG_LAYOUT_STABLE
                                    | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
                                    | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
                                    | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
                                    | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                                    | View.SYSTEM_UI_FLAG_FULLSCREEN;
                            window.getDecorView().setSystemUiVisibility(options);
                        }

                        window.clearFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE);
                    }

                    // 键盘弹出时整体上移
                    {
                        if (false) {
                            //decorView.setSystemUiVisibility(options);
                            //View contentView = window.findViewById(Window.ID_ANDROID_CONTENT);
                            //decorView.getViewTreeObserver().addOnGlobalLayoutListener(getGlobalLayoutListener(decorView, contentView));
                            //window.clearFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE);
                        } else {
                            //decorView.post(() -> {
                            //    Rect rect = new Rect();
                            //    decorView.getWindowVisibleDisplayFrame(rect);
                            //    int diff = decorView.getContext().getResources().getDisplayMetrics().heightPixels - rect.bottom;
                            //    View contentView = window.findViewById(Window.ID_ANDROID_CONTENT);
                            //    if (diff != 0) {
                            //        if (contentView.getPaddingBottom() != diff) {
                            //            contentView.setPadding(0, 0, 0, diff);
                            //        }
                            //    } else {
                            //        if (contentView.getPaddingBottom() != 0) {
                            //            contentView.setPadding(0, 0, 0, 0);
                            //        }
                            //    }
                            //});
                        }
                    }
                }
            }
        }

        /**
         * 设置状态栏背景颜色
         */
        public static void setStatusBarColor(Activity activity, int idResource) {
            if (activity != null && idResource != 0) {
                Window window = activity.getWindow();

                // 取消设置透明状态栏,使ContentView内容不再覆盖状态栏
                window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);

                // 需要设置这个flag才能调用setStatusBarColor来设置状态栏颜色
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                    window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
                }

                // 设置状态栏颜色
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                    window.setStatusBarColor(Display.getColor(activity, idResource));
                }

                ViewGroup contentView = activity.findViewById(Window.ID_ANDROID_CONTENT);
                View childView = contentView.getChildAt(0);
                if (childView != null) {
                    // 注意不是设置ContentView的FitsSystemWindows,而是设置ContentView的第一个子View,预留出系统View的空间
                    ViewCompat.setFitsSystemWindows(childView, true);
                }
            }
        }

        /**
         * 修改屏幕亮度---0.0f(最暗) ~ 1.0f(最亮)
         */
        public static void setScreenBrightness(Activity activity, float brightness) {
            if (activity != null) {
                WindowManager.LayoutParams attribute = activity.getWindow().getAttributes();
                attribute.screenBrightness = Math.min(1.0f, Math.max(0.0f, brightness));
                activity.getWindow().setAttributes(attribute);
            }
        }

        /**
         * 判断当前终端设备是否为iPad
         */
        public static boolean isPad(Context context) {
            return (context.getResources().getConfiguration().screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK) >= Configuration.SCREENLAYOUT_SIZE_LARGE;
        }

        /**
         * 判断当前终端设备是否为Phone
         */
        public static boolean isPhone(Context context) {
            return !isPad(context);
        }

        /**
         * 判断当前终端设备是否为横屏
         */
        public static boolean isLandscape(Context context) {
            return (context.getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE);
        }

        /**
         * 判断当前终端设备是否为竖屏
         */
        public static boolean isPortrait(Context context) {
            return (context.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT);
        }

        /**
         * 判断终端设备状态
         */
        public static boolean isTerminal(Context context, boolean isPad, boolean isLandscape) {
            boolean ok = false;
            {
                Configuration configuration = context.getResources().getConfiguration();
                if (isPad) {
                    if ((configuration.screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK) >= Configuration.SCREENLAYOUT_SIZE_LARGE) {
                        if (isLandscape) {
                            ok = (configuration.orientation == Configuration.ORIENTATION_LANDSCAPE);
                        } else {
                            ok = (configuration.orientation == Configuration.ORIENTATION_PORTRAIT);
                        }
                    }
                } else {
                    if ((configuration.screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK) < Configuration.SCREENLAYOUT_SIZE_LARGE) {
                        if (isLandscape) {
                            ok = (configuration.orientation == Configuration.ORIENTATION_LANDSCAPE);
                        } else {
                            ok = (configuration.orientation == Configuration.ORIENTATION_PORTRAIT);
                        }
                    }
                }
            }
            return ok;
        }

        /**
         * 获取屏幕宽度(包含状态栏)
         */
        public static int getScreenLogicalWidth(Context context) {
            int ok;
            {
                Point outSize = new Point();
                Display.getActivityFromContext(context).getWindowManager().getDefaultDisplay().getRealSize(outSize);
                if (context.getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
                    ok = Math.max(outSize.x, outSize.y);
                } else {
                    ok = Math.min(outSize.x, outSize.y);
                }
            }
            return ok;
        }

        /**
         * 获取屏幕逻辑高度(包含状态栏)
         */
        public static int getScreenLogicalHeight(Context context) {
            int ok;
            {
                Point outSize = new Point();
                Display.getActivityFromContext(context).getWindowManager().getDefaultDisplay().getRealSize(outSize);
                if (context.getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
                    ok = Math.min(outSize.x, outSize.y);
                } else {
                    ok = Math.max(outSize.x, outSize.y);
                }
            }
            return ok;
        }

        /**
         * 获取屏幕宽度(不包含状态栏)
         */
        public static int getScreenWidth(Context context) {
            return context.getResources().getDisplayMetrics().widthPixels;
        }

        /**
         * 获取屏幕高度(不包含状态栏)
         */
        public static int getScreenHeight(Context context) {
            return context.getResources().getDisplayMetrics().heightPixels;
        }

        /**
         * 判断是否为全屏
         */
        public static boolean isFullscreen(Activity activity) {
            return ((activity.getWindow().getDecorView().getSystemUiVisibility() & View.SYSTEM_UI_FLAG_FULLSCREEN) == View.SYSTEM_UI_FLAG_FULLSCREEN);
        }

        /**
         * 判断是否有导航栏
         */
        public static boolean hasNavigationBar(Activity activity) {
            boolean ok = false;
            {
                String NAVIGATION = "navigationBarBackground";
                // 该方法需要在View完全被绘制出来之后调用,否则判断不了,在比如onWindowFocusChanged()方法中可以得到正确的结果
                ViewGroup vp = (ViewGroup) activity.getWindow().getDecorView();
                for (int i = 0; i < vp.getChildCount(); i++) {
                    vp.getChildAt(i).getContext().getPackageName();
                    if (vp.getChildAt(i).getId() != View.NO_ID && NAVIGATION.equals(activity.getResources().getResourceEntryName(vp.getChildAt(i).getId()))) {
                        ok = true;
                    }
                }
            }
            return ok;
        }

        /**
         * 获取导航栏高度
         */
        public static int getNavigationHeight(Activity activity) {
            int ok = 0;
            if (hasNavigationBar(activity)) {
                int resourceId = activity.getResources().getIdentifier("navigation_bar_height", "dimen", "android");
                if (resourceId > 0) {
                    ok = activity.getResources().getDimensionPixelSize(resourceId);
                }
            }
            return ok;
        }

        /**
         * 获取状态栏高度
         */
        public static int getStatusBarHeight(Context context) {
            int ok = 0;
            {
                int resourceId = context.getResources().getIdentifier("status_bar_height", "dimen", "android");
                if (resourceId > 0) {
                    ok = context.getResources().getDimensionPixelSize(resourceId);
                }
            }
            return ok;
        }

        /**
         * 转换分辨率
         */
        public static float getDimension(Context context, int unit, float value) {
            DisplayMetrics dm = context.getResources().getDisplayMetrics();
            switch (unit) {
                case COMPLEX_UNIT_PX:
                    return value;
                case COMPLEX_UNIT_DP:
                    return value * dm.density;
                case COMPLEX_UNIT_SP:
                    return value * dm.scaledDensity;
                case COMPLEX_UNIT_PT:
                    return value * dm.xdpi * (1.0f / 72);
                case COMPLEX_UNIT_IN:
                    return value * dm.xdpi;
                case COMPLEX_UNIT_MM:
                    return value * dm.xdpi * (1.0f / 25.4f);
                default:
                    return 0.0f;
            }
        }

        /**
         * 获取终端的分辨率
         */
        public static float getDensity(Context context) {
            return context.getResources().getDisplayMetrics().density;
        }

        /**
         * 转换分辨率
         */
        public static float getPixel(Context context, float dp) {
            return context.getResources().getDisplayMetrics().density * dp;
        }

        /**
         * 转换分辨率
         */
        public static float getDimension(Context context, int idResource) {
            return context.getResources().getDimension(idResource);
        }

        /**
         * 快速获取资源中的颜色值
         */
        public static int getColor(Context context, int idResource) {
            return (idResource == 0) ? 0 : ContextCompat.getColor(context, idResource);
        }

        /**
         * 快速获取资源中的Drawable
         */
        public static Drawable getDrawable(Context context, int idResource) {
            Drawable ok = null;
            if (idResource != 0) {
                if ((ok = ContextCompat.getDrawable(context, idResource)) != null) {
                    ok.setBounds(0, 0, ok.getMinimumWidth(), ok.getMinimumHeight());
                }
            }
            return ok;
        }

        /**
         * 快速获取资源中的Drawable,并修改颜色
         */
        public static Drawable getDrawable(Context context, int drawableResource, int colorResource) {
            Drawable ok = null;
            if (drawableResource != 0) {
                if ((ok = ContextCompat.getDrawable(context, drawableResource)) != null) {
                    ok.setBounds(0, 0, ok.getMinimumWidth(), ok.getMinimumHeight());
                    if (colorResource != 0) {
                        ok = modifyDrawableResource(context, ok, colorResource);
                    }
                }
            }
            return ok;
        }

        /**
         * 修改Drawable的颜色
         */
        public static Drawable modifyDrawableResource(Context context, Drawable drawable, int colorResource) {
            Drawable ok = drawable;
            if ((drawable != null) && (colorResource != 0)) {
                ok = modifyDrawableColor(drawable, Display.getColor(context, colorResource));
            }
            return ok;
        }

        /**
         * 修改Drawable的颜色
         */
        public static Drawable modifyDrawableColor(Drawable drawable, int color) {
            Drawable ok = drawable;
            if (drawable != null) {
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                    ok.setColorFilter(color, PorterDuff.Mode.SRC_ATOP);
                } else {
                    ok = DrawableCompat.wrap(drawable);
                    DrawableCompat.setTintMode(ok, PorterDuff.Mode.SRC_ATOP);
                    DrawableCompat.setTint(ok, color);
                }
            }
            return ok;
        }

        /**
         * 快速获取资源中的颜色值
         */
        public static Drawable getAttributeDrawable(Context context, int idResource) {
            Drawable ok = null;
            if (idResource != 0) {
                TypedValue typedValue = new TypedValue();
                context.getTheme().resolveAttribute(idResource, typedValue, true);
                int[] attribute = new int[]{idResource};
                TypedArray typedArray = context.getTheme().obtainStyledAttributes(typedValue.resourceId, attribute);
                if ((ok = typedArray.getDrawable(0)) != null) {
                    ok.setBounds(0, 0, ok.getMinimumWidth(), ok.getMinimumHeight());
                }
                typedArray.recycle();
            }
            return ok;
        }

        /**
         * 回收ImageView内存
         */
        public static void recycle(ImageView view, boolean foreground) {
            if (view != null) {
                Drawable drawable = foreground ? view.getDrawable() : view.getBackground();
                if (drawable instanceof BitmapDrawable) {
                    Bitmap bitmap = ((BitmapDrawable) drawable).getBitmap();
                    if (bitmap != null && !bitmap.isRecycled()) {
                        bitmap.recycle();
                    }
                }
            }
        }

        /**
         * 快速获取资源中的字符串
         */
        public static String getString(Context context, int idResource) {
            return (idResource == 0) ? "" : context.getResources().getString(idResource);
        }

        /**
         * 快速获取资源中的字符串数组
         */
        public static String[] getStringArray(Context context, int idResource) {
            return (idResource == 0) ? new String[0] : context.getResources().getStringArray(idResource);
        }

        /**
         * 快速获取资源中的字符串数组中的一项
         */
        public static String getStringArray(Context context, int idResource, int index) {
            return (idResource == 0) ? "" : context.getResources().getStringArray(idResource)[index];
        }

        /**
         * 获取视图的在窗口中的矩形
         */
        public static Rect getViewWindowRect(View view) {
            Rect ok = new Rect();
            if (view != null) {
                int[] location = new int[2];
                view.getLocationInWindow(location);
                ok.left = location[0];
                ok.top = location[1];
                ok.right = ok.left + view.getWidth();
                ok.bottom = ok.top + view.getHeight();
            }
            return ok;
        }

        /**
         * 获取视图的左上角在窗口中的坐标
         */
        public static Point getViewWindowLocation(View view) {
            Point ok = new Point();
            if (view != null) {
                int[] location = new int[2];
                view.getLocationInWindow(location);
                ok.x = location[0];
                ok.y = location[1];
            }
            return ok;
        }

        /**
         * 获取视图的中心点在窗口中的坐标
         */
        public static Point getViewWindowCenter(View view) {
            Point ok = new Point();
            if (view != null) {
                int[] location = new int[2];
                view.getLocationInWindow(location);
                ok.x = location[0] + view.getWidth() / 2;
                ok.y = location[1] + view.getHeight() / 2;
            }
            return ok;
        }

        /**
         * 画字符串
         * alignX: 0=左对齐; 1=居中; 2=右对齐
         * alignY: 0=上对齐; 1=居中; 2=下对齐
         */
        public static void drawText(Canvas canvas, String text, float dx, float dy, Paint paint, int alignX, int alignY) {
            if (!text.isEmpty()) {
                switch (alignX) {
                    case 0:
                        paint.setTextAlign(Paint.Align.LEFT);
                        break;
                    default:
                        paint.setTextAlign(Paint.Align.CENTER);
                        break;
                    case 2:
                        paint.setTextAlign(Paint.Align.RIGHT);
                        break;
                }
                Paint.FontMetrics fm = paint.getFontMetrics();

                //float height = fm.bottom - fm.top;
                //float baseline = dy - (fm.top + fm.bottom) / 2.0f;

                // 以下方法更准确
                float height = fm.descent - fm.ascent;
                float baseline = dy + height / 2.0f - fm.descent;

                switch (alignY) {
                    case 0:
                        baseline += height / 2.0f;
                        break;
                    case 2:
                        baseline -= height / 2.0f;
                        break;
                }

                canvas.drawText(text, dx, baseline, paint);
            }
        }

        /**
         * 画字符串
         * alignX:     0=左对齐; 1=居中; 2=右对齐
         * alignY:     0=上对齐; 1=居中; 2=下对齐
         * limit:      位置限制矩形,若超出,将自动更改对齐方式
         * horizontal: 限制横向?
         */
        public static void drawText(Canvas canvas, String text, float dx, float dy, Paint paint, int alignX, int alignY, RectF limit, boolean horizontal) {
            if (!text.isEmpty()) {
                Paint.FontMetrics fm = paint.getFontMetrics();
                float width = paint.measureText(text);
                //float height = fm.bottom - fm.top;
                //float baseline = dy - (fm.top + fm.bottom) / 2.0f;
                // 以下方法更准确
                float height = fm.descent - fm.ascent;
                float baseline = dy + height / 2.0f - fm.descent;
                float halfW = width / 2.0f;
                float halfH = height / 2.0f;

                if (horizontal) {
                    switch (alignX) {
                        case 0:
                            if (dx + width > limit.right) {
                                dx = limit.right;
                                alignX = 2;
                            }
                            break;
                        case 1:
                            if (dx - halfW < limit.left) {
                                dx = limit.left;
                                alignX = 0;
                            } else if (dx + halfW > limit.right) {
                                dx = limit.right;
                                alignX = 2;
                            }
                            break;
                        case 2:
                            if (dx - width < limit.left) {
                                dx = limit.left;
                                alignX = 0;
                            }
                            break;
                    }
                } else {
                    switch (alignY) {
                        case 0:
                            if (dy + height > limit.bottom) {
                                dy = limit.bottom;
                                alignY = 2;
                            }
                            break;
                        case 1:
                            if (dy - halfH < limit.top) {
                                dy = limit.top;
                                alignY = 0;
                            } else if (dy + halfH > limit.bottom) {
                                dy = limit.bottom;
                                alignY = 2;
                            }
                            break;
                        case 2:
                            if (dy - height < limit.top) {
                                dy = limit.top;
                                alignY = 0;
                            }
                            break;
                    }
                }

                switch (alignX) {
                    case 0:
                        paint.setTextAlign(Paint.Align.LEFT);
                        break;
                    default:
                        paint.setTextAlign(Paint.Align.CENTER);
                        break;
                    case 2:
                        paint.setTextAlign(Paint.Align.RIGHT);
                        break;
                }

                switch (alignY) {
                    case 0:
                        baseline += halfH;
                        break;
                    case 2:
                        baseline -= halfH;
                        break;
                }

                canvas.drawText(text, dx, baseline, paint);
            }
        }

        /**
         * 在限制矩形区域内,保证不画出一半宽度或高度的线条
         */
        public static void drawLine(Canvas canvas, float startX, float startY, float stopX, float stopY, Paint paint, RectF limit) {
            if (startX == stopX) {
                if (startX == limit.left) {
                    startX += paint.getStrokeWidth() / 2.0f;
                    stopX += paint.getStrokeWidth() / 2.0f;
                } else if (startX == limit.right) {
                    startX -= paint.getStrokeWidth() / 2.0f;
                    stopX -= paint.getStrokeWidth() / 2.0f;
                }
            }
            if (startY == stopY) {
                if (startY == limit.top) {
                    startY += paint.getStrokeWidth() / 2.0f;
                    stopY += paint.getStrokeWidth() / 2.0f;
                } else if (startY == limit.bottom) {
                    startY -= paint.getStrokeWidth() / 2.0f;
                    stopY -= paint.getStrokeWidth() / 2.0f;
                }
            }
            canvas.drawLine(startX, startY, stopX, stopY, paint);
        }

        private static Activity getActivityFromContext(Context context) {
            if (context == null) {
                return null;
            }
            if (context instanceof Activity) {
                return (Activity) context;
            }
            if (context instanceof Application || context instanceof Service) {
                return null;
            }
            Context c = context;
            while (c != null) {
                if (c instanceof ContextWrapper) {
                    c = ((ContextWrapper) c).getBaseContext();
                    if (c instanceof Activity) {
                        return (Activity) c;
                    }
                } else {
                    return null;
                }
            }
            return null;
        }
    }

    /**
     * Base64编解码
     */
    public static class Base64 {

        /**
         * 将文件编码为Base64字符串
         * nowrap: 是否去掉换行符
         */
        public static String fileToBase64(String file, boolean nowrap) {
            return Base64.fileToBase64(new File(file), nowrap);
        }

        /**
         * 将文件编码为Base64字符串
         * nowrap: 是否去掉换行符
         */
        public static String fileToBase64(File file, boolean nowrap) {
            String ok = "";
            try {
                FileInputStream stream = new FileInputStream(file);
                if (stream.available() > 0) {
                    byte[] data = new byte[stream.available()];
                    if (stream.read(data) == data.length) {
                        ok = Base64.toBase64(data, nowrap);
                    }
                }
                stream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            return ok;
        }

        /**
         * 将字符数组编码为Base64字符串
         * nowrap: 是否去掉换行符
         */
        public static String toBase64(byte[] data, boolean nowrap) {
            String ok = null;
            if (data != null) {
                if (!nowrap) {
                    ok = android.util.Base64.encodeToString(data, android.util.Base64.DEFAULT);
                } else {
                    String encode = android.util.Base64.encodeToString(data, android.util.Base64.DEFAULT);
                    ok = encode.replaceAll("\n", "").replaceAll("\r", "");
                }
            }
            return ok;
        }

        /**
         * 解码Base64字符串到文件
         */
        public static void base64ToFile(String data, String file) {
            Base64.base64ToFile(data, new File(file));
        }

        /**
         * 解码Base64字符串到文件
         */
        public static void base64ToFile(String data, File file) {
            if ((data != null) && (data.length() > 0)) {
                try {
                    FileOutputStream stream = new FileOutputStream(file);
                    stream.write(Base64.fromBase64(data));
                    stream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

        /**
         * 解码Base64字符串
         */
        public static byte[] fromBase64(String data) {
            byte[] ok = null;
            if (!Str.isEmpty(data) && (data.length() >= 4)) {
                ok = android.util.Base64.decode(data, android.util.Base64.DEFAULT);
            }
            return ok;
        }
    }

    /**
     * 文件IO相关操作的封装
     */
    public static class IO {

        /**
         * 排序规则
         */
        public static final int SORT_FILE_NAME = 0;
        public static final int SORT_FILE_LAST_MODIFY = 1;
        public static final int SORT_FILE_SIZE = 2;

        /**
         * 获取外部存储器的根目录
         */
        public static File getRootPath() {
            File ok = null;
            if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) {   //检查是否存在SDCard
                ok = Environment.getExternalStorageDirectory();
            }
            return ok;
        }

        /**
         * 获取外部存储器的追加目录
         */
        public static String getRootPath(String append) {
            String ok = IO.getRootPath().toString();
            if (!Str.isEmpty(append)) {
                ok = ok + File.separator + append;
            }
            return ok;
        }

        /**
         * 获取系统路径,主要用于assets资源的复制
         */
        public static String getSystemPath(Context context) {
            return context.getFilesDir().getPath();
        }

        /**
         * 获取缓存路径
         */
        public static String getCacheDir(Context context) {
            String ok;
            // 判断是否有SD卡
            if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()) || !Environment.isExternalStorageRemovable()) {
                ok = context.getExternalCacheDir().getPath();
            } else {
                ok = context.getCacheDir().getPath();
            }
            return ok;
        }

        /**
         * 获取U盘路径
         */
        public static List<String> getUSBFlash(Context context) {
            List<String> ok = new ArrayList<>();
            StorageManager manager = (StorageManager) context.getSystemService(Context.STORAGE_SERVICE);
            if (manager != null) {
                try {
                    String[] paths = (String[]) manager.getClass().getMethod("getVolumePaths").invoke(manager);
                    for (String path : paths) {
                        String state = (String) manager.getClass().getMethod("getVolumeState", String.class).invoke(manager, path);
                        if (state.equals(Environment.MEDIA_MOUNTED) && !path.contains("emulated")) {
                            ok.add(path);
                        }
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            return ok;
        }

        /**
         * 判断文件是否存在
         */
        public static boolean isExist(String file) {
            return new File(file).exists();
        }

        /**
         * 判断路径是否为文件夹
         */
        public static boolean isDirectory(String file) {
            return new File(file).isDirectory();
        }

        /**
         * 判断路径是否为文件
         */
        public static boolean isFile(String file) {
            return new File(file).isFile();
        }

        /**
         * 判断文件扩展名
         */
        public static boolean isExtension(String file, String... extensions) {
            boolean ok = false;
            if (!Str.isEmpty(file) && (extensions != null)) {
                String lower = file.toLowerCase();
                for (String extension : extensions) {
                    if (lower.endsWith(extension)) {
                        ok = true;
                        break;
                    }
                }
            }
            return ok;
        }

        /**
         * 创建路径
         */
        public static boolean mkdirs(String path) {
            boolean ok;
            {
                File file = new File(path);
                if (!(ok = file.exists())) {
                    ok = file.mkdirs();
                }
            }
            return ok;
        }

        /**
         * 获取文件名
         */
        public static String getName(String file, boolean containExtension) {
            String ok = new File(file).getName();
            if (!containExtension) {
                int index = ok.lastIndexOf(".");
                if (index >= 0) ok = ok.substring(0, index);
            }
            return ok;
        }

        /**
         * 删除文件/文件夹
         */
        public static boolean deleteFileAndFolder(String path, boolean deleteParentIfEmpty) {
            return deleteFileAndFolder(new File(path), deleteParentIfEmpty);
        }

        /**
         * 删除文件/文件夹
         */
        public static boolean deleteFileAndFolder(File path, boolean deleteParentIfEmpty) {
            boolean ok = false;
            if (path != null) {
                if (path.exists()) {
                    if (path.isFile()) {
                        ok = path.delete();
                    } else if (path.isDirectory()) {
                        ok = true;
                        File[] files = path.listFiles();
                        if (files != null && files.length != 0) {
                            for (File file : files) {
                                if (file.isFile()) {
                                    if (!(ok = file.delete())) break;
                                } else if (file.isDirectory()) {
                                    if (!(ok = deleteFileAndFolder(file))) break;
                                }
                            }
                        }
                        if (deleteParentIfEmpty && ok) ok = path.delete();
                    }
                    ok = true;
                }
            }
            return ok;
        }

        /**
         * 删除前面的根目录,以缩短文件名
         */
        public static String removeRoot(String file) {
            String ok = file;
            if (!file.isEmpty()) {
                String root = IO.getRootPath().toString();
                if (file.indexOf(root) == 0) {
                    ok = file.substring(root.length());
                }
            }
            return ok;
        }

        /**
         * 删除空文件夹
         */
        public static void deleteEmptyFolder(String folder) {
            File file = new File(folder);
            if (file.exists() && file.isDirectory()) {
                File[] list = file.listFiles();
                if (list != null) {
                    if (list.length == 0) file.delete();
                }
            }
        }

        /**
         * 获取文件夹下所有文件列表
         */
        public static List<String> getFiles(String folder, int sortMode, boolean asc) {
            List<String> ok = new ArrayList<>();
            if (folder != null) {
                File[] files = new File(folder).listFiles();
                if (files != null) {
                    // 按文件名排序
                    if (sortMode == SORT_FILE_NAME) {
                        List<File> list = Arrays.asList(files);
                        Collections.sort(list, (o1, o2) -> {
                            if (o1.isDirectory() && o2.isFile())
                                return -1;
                            if (o1.isFile() && o2.isDirectory())
                                return 1;
                            if (asc) return o1.getName().compareTo(o2.getName());
                            else return o2.getName().compareTo(o1.getName());
                        });
                    } else if (sortMode == SORT_FILE_LAST_MODIFY) {
                        List<File> list = Arrays.asList(files);
                        Collections.sort(list, (o1, o2) -> {
                            long diff = o1.lastModified() - o2.lastModified();
                            if (diff > 0)
                                return asc ? 1 : -1;
                            else if (diff == 0)
                                return 0;
                            else
                                return asc ? -1 : 1;
                        });
                    } else if (sortMode == SORT_FILE_SIZE) {
                        List<File> list = Arrays.asList(files);
                        Collections.sort(list, (o1, o2) -> {
                            long diff = o1.length() - o2.length();
                            if (diff > 0)
                                return asc ? 1 : -1;
                            else if (diff == 0)
                                return 0;
                            else
                                return asc ? -1 : 1;
                        });
                    }

                    // 准备输出
                    for (File file : files) ok.add(file.getPath());
                }
            }
            return ok;
        }

        /**
         * 复制assets
         */
        public static void copyAssetsToDest(AssetManager assetManager, final String src, final String dst) {
            try {
                String[] list = assetManager.list(src);
                if ((list != null) && (list.length > 0)) {
                    File to = new File(dst);
                    if (to.exists() || to.mkdirs()) {
                        for (String name : list) {
                            copyAssetsToDest(assetManager, src + "/" + name, dst + "/" + name);
                        }
                    }
                } else {
                    BufferedInputStream from = new BufferedInputStream(assetManager.open(src));
                    BufferedOutputStream to = new BufferedOutputStream(new FileOutputStream(dst));
                    byte[] bt = new byte[65536];
                    int read;
                    while ((read = from.read(bt)) != -1) {
                        to.write(bt, 0, read);
                    }
                    to.flush();
                    to.close();
                    from.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        /**
         * 保存RAW文件
         */
        public static void saveRawFile(Context context, int rawResource, File path, String name) {
            if (path.exists() || path.mkdirs()) {
                try {
                    InputStream is = context.getResources().openRawResource(rawResource);
                    OutputStream os = new FileOutputStream(new File(path, name));

                    byte[] buffer = new byte[4096];
                    int bytesRead;
                    while ((bytesRead = is.read(buffer)) != -1) {
                        os.write(buffer, 0, bytesRead);
                    }

                    os.flush();
                    is.close();
                    os.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

        // 删除文件/文件夹
        private static boolean deleteFileAndFolder(File folder) {
            boolean ok = true;
            if ((folder != null) && folder.exists()) {
                if (folder.isDirectory()) {
                    File[] files = folder.listFiles();
                    if (files != null && files.length != 0) {
                        for (File file : files) {
                            if (file.isFile()) {
                                if (!(ok = file.delete())) break;
                            } else if (file.isDirectory()) {
                                if (!(ok = deleteFileAndFolder(file))) break;
                            }
                        }
                    }
                }
                if (ok) ok = folder.delete();
            }
            return ok;
        }
    }

    /**
     * 通过Uri获取文件路径
     */
    public static class Uri {

        public static String getFilePathFromURI(Context context, android.net.Uri contentUri) {
            File rootDataDir = context.getExternalFilesDir(null);
            String fileName = getFileName(contentUri);
            if (!Str.isEmpty(fileName)) {
                File copyFile = new File(rootDataDir + File.separator + fileName);
                copyFile(context, contentUri, copyFile);
                return copyFile.getAbsolutePath();
            }
            return null;
        }

        public static String getFileName(android.net.Uri uri) {
            if (uri == null) return null;
            String fileName = null;
            String path = uri.getPath();
            int cut = path.lastIndexOf('/');
            if (cut != -1) {
                fileName = path.substring(cut + 1);
            }
            return fileName;
        }

        public static void copyFile(Context context, android.net.Uri srcUri, File dstFile) {
            try {
                InputStream inputStream = context.getContentResolver().openInputStream(srcUri);
                if (inputStream == null) return;
                OutputStream outputStream = new FileOutputStream(dstFile);
                copyStream(inputStream, outputStream);
                inputStream.close();
                outputStream.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        public static int copyStream(InputStream input, OutputStream output) throws Exception, IOException {
            final int BUFFER_SIZE = 1024 * 2;
            byte[] buffer = new byte[BUFFER_SIZE];
            BufferedInputStream in = new BufferedInputStream(input, BUFFER_SIZE);
            BufferedOutputStream out = new BufferedOutputStream(output, BUFFER_SIZE);
            int count = 0, n = 0;
            try {
                while ((n = in.read(buffer, 0, BUFFER_SIZE)) != -1) {
                    out.write(buffer, 0, n);
                    count += n;
                }
                out.flush();
            } finally {
                try {
                    out.close();
                } catch (IOException e) {
                }
                try {
                    in.close();
                } catch (IOException e) {
                }
            }
            return count;
        }

        /**
         * 通过uri获取文件路径
         */
        public static String getFileAbsolutePath(Context context, android.net.Uri uri) {
            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
                return getRealFilePath(context, uri);
            }

            if ((Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) && (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) && DocumentsContract.isDocumentUri(context, uri)) {
                if (isExternalStorageDocument(uri)) {
                    String docId = DocumentsContract.getDocumentId(uri);
                    String[] split = docId.split(":");
                    String type = split[0];
                    if ("primary".equalsIgnoreCase(type)) {
                        return Environment.getExternalStorageDirectory() + "/" + split[1];
                    }
                } else if (isDownloadsDocument(uri)) {
                    String id = DocumentsContract.getDocumentId(uri);
                    android.net.Uri contentUri = ContentUris.withAppendedId(android.net.Uri.parse("content://downloads/public_downloads"), Long.parseLong(id));
                    return getDataColumn(context, contentUri, null, null);
                } else if (isMediaDocument(uri)) {
                    String docId = DocumentsContract.getDocumentId(uri);
                    String[] split = docId.split(":");
                    String type = split[0];
                    android.net.Uri contentUri = null;
                    if ("image".equals(type)) {
                        contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
                    } else if ("video".equals(type)) {
                        contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
                    } else if ("audio".equals(type)) {
                        contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
                    }
                    String selection = MediaStore.Images.Media._ID + "=?";
                    String[] selectionArgs = new String[]{split[1]};
                    return getDataColumn(context, contentUri, selection, selectionArgs);
                }
            }

            // MediaStore (and general)
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
                return uriToFileApiQ(context, uri);
            } else if ("content".equalsIgnoreCase(uri.getScheme())) {
                // Return the remote address
                if (isGooglePhotosUri(uri)) {
                    return uri.getLastPathSegment();
                }
                return getDataColumn(context, uri, null, null);
            } else if ("file".equalsIgnoreCase(uri.getScheme())) {  // File
                return uri.getPath();
            }

            return null;
        }

        // 此方法只能用于4.4以下的版本
        private static String getRealFilePath(final Context context, final android.net.Uri uri) {
            String data = null;
            {
                final String scheme = uri.getScheme();
                if (scheme == null) {
                    data = uri.getPath();
                } else if (ContentResolver.SCHEME_FILE.equals(scheme)) {
                    data = uri.getPath();
                } else if (ContentResolver.SCHEME_CONTENT.equals(scheme)) {
                    String[] projection = {MediaStore.Images.ImageColumns.DATA};
                    Cursor cursor = context.getContentResolver().query(uri, projection, null, null, null);
                    if (null != cursor) {
                        if (cursor.moveToFirst()) {
                            int index = cursor.getColumnIndex(MediaStore.Images.ImageColumns.DATA);
                            if (index > -1) data = cursor.getString(index);
                        }
                        cursor.close();
                    }
                }
            }
            return data;
        }


        /**
         * @param uri The Uri to check.
         * @return Whether the Uri authority is ExternalStorageProvider.
         */
        private static boolean isExternalStorageDocument(android.net.Uri uri) {
            return "com.android.externalstorage.documents".equals(uri.getAuthority());
        }

        /**
         * @param uri The Uri to check.
         * @return Whether the Uri authority is DownloadsProvider.
         */
        private static boolean isDownloadsDocument(android.net.Uri uri) {
            return "com.android.providers.downloads.documents".equals(uri.getAuthority());
        }

        private static String getDataColumn(Context context, android.net.Uri uri, String selection, String[] selectionArgs) {
            Cursor cursor = null;
            String column = MediaStore.Images.Media.DATA;
            String[] projection = {column};
            try {
                cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs, null);
                if (cursor != null && cursor.moveToFirst()) {
                    int index = cursor.getColumnIndexOrThrow(column);
                    return cursor.getString(index);
                }
            } finally {
                if (cursor != null) {
                    cursor.close();
                }
            }
            return null;
        }

        /**
         * @param uri The Uri to check.
         * @return Whether the Uri authority is MediaProvider.
         */
        private static boolean isMediaDocument(android.net.Uri uri) {
            return "com.android.providers.media.documents".equals(uri.getAuthority());
        }

        /**
         * @param uri The Uri to check.
         * @return Whether the Uri authority is Google Photos.
         */
        private static boolean isGooglePhotosUri(android.net.Uri uri) {
            return "com.google.android.apps.photos.content".equals(uri.getAuthority());
        }

        /**
         * Android10以上适配另一种写法
         */
        private static String getFileFromContentUri(Context context, android.net.Uri uri) {
            String filePath;
            String[] filePathColumn = {MediaStore.MediaColumns.DATA, MediaStore.MediaColumns.DISPLAY_NAME};
            ContentResolver contentResolver = context.getContentResolver();
            Cursor cursor = contentResolver.query(uri, filePathColumn, null, null, null);
            if (cursor != null) {
                cursor.moveToFirst();
                try {
                    filePath = cursor.getString(cursor.getColumnIndexOrThrow(filePathColumn[0]));
                    return filePath;
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    cursor.close();
                }
            }
            return "";
        }

        /**
         * Android10以上适配
         */
        @RequiresApi(api = Build.VERSION_CODES.Q)
        private static String uriToFileApiQ(Context context, android.net.Uri uri) {
            String ok = null;
            {
                // android10以上转换
                if (uri.getScheme().equals(ContentResolver.SCHEME_FILE)) {
                    ok = new File(uri.getPath()).getAbsolutePath();
                } else if (uri.getScheme().equals(ContentResolver.SCHEME_CONTENT)) {
                    // 把文件复制到沙盒目录
                    ContentResolver contentResolver = context.getContentResolver();
                    Cursor cursor = contentResolver.query(uri, null, null, null, null);
                    if (cursor.moveToFirst()) {
                        String displayName = cursor.getString(cursor.getColumnIndexOrThrow(OpenableColumns.DISPLAY_NAME));
                        try {
                            InputStream is = contentResolver.openInputStream(uri);
                            File cache = new File(context.getExternalCacheDir().getAbsolutePath(), displayName);
                            FileOutputStream fos = new FileOutputStream(cache);
                            FileUtils.copy(is, fos);
                            ok = cache.getAbsolutePath();
                            fos.close();
                            is.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
            return ok;
        }
    }

    /**
     * 数据库操作
     */
    public static class SharedPreferences {

        private static class Holder {
            private static SharedPreferences instance = new SharedPreferences();
        }

        private android.content.SharedPreferences m_preferences;

        /**
         * 使用前必须先初始化
         */
        public static void init(Context context, String name) {
            if (Holder.instance.m_preferences == null) {
                Holder.instance.m_preferences = context.getApplicationContext().getSharedPreferences(name, Context.MODE_PRIVATE);
            }
        }

        public static boolean isExist(String key) {
            return (Holder.instance.m_preferences != null) && Holder.instance.m_preferences.contains(key);
        }

        public static Map<String, ?> getAll() {
            Map<String, ?> ok = new HashMap<>();
            if (Holder.instance.m_preferences != null) {
                ok = Holder.instance.m_preferences.getAll();
            }
            return ok;
        }

        public static void remove(String key) {
            if (Holder.instance.m_preferences != null) {
                android.content.SharedPreferences.Editor editor = Holder.instance.m_preferences.edit();
                editor.remove(key);
                editor.commit();
            }
        }

        public static boolean setValue(String key, boolean value) {
            boolean ok = false;
            if (Holder.instance.m_preferences != null) {
                android.content.SharedPreferences.Editor editor = Holder.instance.m_preferences.edit();
                editor.putBoolean(key, value);
                ok = editor.commit();
            }
            return ok;
        }

        public static boolean getValue(String key, boolean defValue) {
            boolean ok = false;
            if (Holder.instance.m_preferences != null) {
                ok = Holder.instance.m_preferences.getBoolean(key, defValue);
            }
            return ok;
        }

        public static boolean setValue(String key, short value) {
            boolean ok = false;
            if (Holder.instance.m_preferences != null) {
                android.content.SharedPreferences.Editor editor = Holder.instance.m_preferences.edit();
                editor.putInt(key, value);
                ok = editor.commit();
            }
            return ok;
        }

        public static short getValue(String key, short defValue) {
            short ok = defValue;
            if (Holder.instance.m_preferences != null) {
                ok = (short) Holder.instance.m_preferences.getInt(key, defValue);
            }
            return ok;
        }

        public static boolean setValue(String key, int value) {
            boolean ok = false;
            if (Holder.instance.m_preferences != null) {
                android.content.SharedPreferences.Editor editor = Holder.instance.m_preferences.edit();
                editor.putInt(key, value);
                ok = editor.commit();
            }
            return ok;
        }

        public static int getValue(String key, int defValue) {
            int ok = defValue;
            if (Holder.instance.m_preferences != null) {
                ok = Holder.instance.m_preferences.getInt(key, defValue);
            }
            return ok;
        }

        public static boolean setValue(String key, long value) {
            boolean ok = false;
            if (Holder.instance.m_preferences != null) {
                android.content.SharedPreferences.Editor editor = Holder.instance.m_preferences.edit();
                editor.putLong(key, value);
                ok = editor.commit();
            }
            return ok;
        }

        public static long getValue(String key, long defValue) {
            long ok = defValue;
            if (Holder.instance.m_preferences != null) {
                ok = Holder.instance.m_preferences.getLong(key, defValue);
            }
            return ok;
        }

        public static boolean setValue(String key, float value) {
            boolean ok = false;
            if (Holder.instance.m_preferences != null) {
                android.content.SharedPreferences.Editor editor = Holder.instance.m_preferences.edit();
                editor.putFloat(key, value);
                ok = editor.commit();
            }
            return ok;
        }

        public static float getValue(String key, float defValue) {
            float ok = defValue;
            if (Holder.instance.m_preferences != null) {
                ok = Holder.instance.m_preferences.getFloat(key, defValue);
            }
            return ok;
        }

        public static boolean setValue(String key, String value) {
            boolean ok = false;
            if (Holder.instance.m_preferences != null) {
                android.content.SharedPreferences.Editor editor = Holder.instance.m_preferences.edit();
                editor.putString(key, value);
                ok = editor.commit();
            }
            return ok;
        }

        public static String getValue(String key, String defValue) {
            String ok = defValue;
            if (Holder.instance.m_preferences != null) {
                ok = Holder.instance.m_preferences.getString(key, defValue);
            }
            return ok;
        }

        public static boolean setValue(String key, float[] array) {
            boolean ok = false;
            if (Holder.instance.m_preferences != null) {
                try {
                    android.content.SharedPreferences.Editor editor = Holder.instance.m_preferences.edit();
                    JSONArray jsonArray = new JSONArray();
                    for (float value : array) jsonArray.put(value);
                    editor.putString(key, jsonArray.toString());
                    ok = editor.commit();
                } catch (JSONException e) {
                    e.printStackTrace();
                }
            }
            return ok;
        }

        public static float[] getValue(String key, float[] defValue) {
            float[] ok = defValue;
            if (Holder.instance.m_preferences != null) {
                try {
                    JSONArray jsonArray = new JSONArray(Holder.instance.m_preferences.getString(key, "[]"));
                    if (jsonArray.length() > 0) {
                        ok = new float[jsonArray.length()];
                        for (int n = 0; n < jsonArray.length(); ++n) {
                            ok[n] = (float) jsonArray.getDouble(n);
                        }
                    }
                } catch (JSONException e) {
                    e.printStackTrace();
                }
            }
            return ok;
        }

        public static boolean setValue(String key, String[] array) {
            boolean ok = false;
            if (Holder.instance.m_preferences != null) {
                android.content.SharedPreferences.Editor editor = Holder.instance.m_preferences.edit();
                JSONArray jsonArray = new JSONArray();
                for (String value : array) jsonArray.put(value);
                editor.putString(key, jsonArray.toString());
                ok = editor.commit();
            }
            return ok;
        }

        public static String[] getValue(String key, String[] defValue) {
            String[] ok = defValue;
            if (Holder.instance.m_preferences != null) {
                try {
                    JSONArray jsonArray = new JSONArray(Holder.instance.m_preferences.getString(key, "[]"));
                    if (jsonArray.length() > 0) {
                        ok = new String[jsonArray.length()];
                        for (int n = 0; n < jsonArray.length(); ++n) {
                            ok[n] = jsonArray.getString(n);
                        }
                    }
                } catch (JSONException e) {
                    e.printStackTrace();
                }
            }
            return ok;
        }

        public static boolean setValueSet(String key, Set<String> value) {
            boolean ok = false;
            if (Holder.instance.m_preferences != null) {
                android.content.SharedPreferences.Editor editor = Holder.instance.m_preferences.edit();
                editor.putStringSet(key, value);
                ok = editor.commit();
            }
            return ok;
        }

        public static Set<String> getValueSet(String key) {
            Set<String> ok = null;
            if (Holder.instance.m_preferences != null) {
                ok = Holder.instance.m_preferences.getStringSet(key, null);
            }
            return ok;
        }
    }

    /**
     * 分享功能的实现
     */
    public static class Share {

        public static void share(Context context, String file) {
            Share.share(context, new File(file));
        }

        public static void share(Context context, File file) {
            // 分享前必须执行本代码,以兼容SDK18以上的系统
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
                StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder();
                StrictMode.setVmPolicy(builder.build());
                builder.detectFileUriExposure();
            }

            // 分享
            android.content.Intent intent = new android.content.Intent(android.content.Intent.ACTION_SEND);
            {
                if (IO.isExtension(file.getPath(), ".bmp", ".png", ".jpg", ".jpeg", ".gif", ".dcm", ".dicom"))
                    intent.setType("image/*");
                else if (IO.isExtension(file.getPath(), ".avi", ".mp4", ".mkv", ".3gp"))
                    intent.setType("video/*");
                else
                    intent.setType("*/*");
                intent.putExtra(android.content.Intent.EXTRA_SUBJECT, file.getName());
                intent.putExtra(android.content.Intent.EXTRA_TEXT, file.getName());
                intent.putExtra(android.content.Intent.EXTRA_STREAM, android.net.Uri.fromFile(file));
                intent.setFlags(android.content.Intent.FLAG_ACTIVITY_NEW_TASK);
            }
            context.startActivity(android.content.Intent.createChooser(intent, "Share"));
        }

        public static void shareText(Context context, String text) {
            if (!Str.isEmpty(text)) {
                // 分享前必须执行本代码,以兼容SDK18以上的系统
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
                    StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder();
                    StrictMode.setVmPolicy(builder.build());
                    builder.detectFileUriExposure();
                }

                // 分享
                android.content.Intent intent = new android.content.Intent(android.content.Intent.ACTION_SEND);
                {
                    intent.putExtra(android.content.Intent.EXTRA_TEXT, text);
                    intent.setType("text/plain");
                }
                context.startActivity(android.content.Intent.createChooser(intent, "Share"));
            }
        }
    }

    /**
     * 弹出式消息对话框
     */
    public static class DialogMessage {

        public interface OnClickListener {
            void onClick();
        }

        public static void show(Context context, int messageResource, int confirmResource, boolean darkBackground, boolean fullScreen, OnClickListener listener) {
            DialogMessage.show(context, 0, messageResource, confirmResource, darkBackground, fullScreen, listener);
        }

        public static void show(Context context, String message, String confirm, boolean darkBackground, boolean fullScreen, OnClickListener listener) {
            DialogMessage.show(context, null, message, confirm, darkBackground, fullScreen, listener);
        }

        public static void show(Context context, int titleResource, int messageResource, int confirmResource, boolean darkBackground, boolean fullScreen, OnClickListener listener) {
            DialogMessage.show(context,
                    Display.getString(context, titleResource),
                    Display.getString(context, messageResource),
                    Display.getString(context, confirmResource),
                    darkBackground, fullScreen,
                    listener);
        }

        public static void show(Context context, String title, String message, String confirm, boolean darkBackground, boolean fullScreen, OnClickListener listener) {
            TextView titleView = null;
            TextView msgView = new TextView(context);

            AlertDialog dialog = new AlertDialog.Builder(context)
                    .setView(msgView)
                    .setPositiveButton(confirm, (dialogInterface, i) -> {
                        if (listener != null) listener.onClick();
                    })
                    .setCancelable(false)
                    .create();

            if (!Str.isEmpty(title)) dialog.setCustomTitle(titleView = new TextView(context));

            // 显示对话框
            Window window = dialog.getWindow();
            if (window == null) {
                dialog.show();
            } else {
                if (!darkBackground) {
                    window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
                }
                Display.setNavigationBarAndStatusBar(dialog, fullScreen);
            }

            // title
            if (titleView != null) {
                titleView.setPaddingRelative((int) Display.getDimension(context, R.dimen.size_16), (int) Display.getDimension(context, R.dimen.size_8), (int) Display.getDimension(context, R.dimen.size_16), (int) Display.getDimension(context, R.dimen.size_8));
                titleView.setGravity(Gravity.START | Gravity.CENTER_VERTICAL);
                titleView.setTextColor(Color.BLACK);
                titleView.setText(title);
                titleView.setTextSize(TypedValue.COMPLEX_UNIT_PX, Display.getDimension(context, R.dimen.text_size_16));
                titleView.setPaintFlags(Paint.FAKE_BOLD_TEXT_FLAG | Paint.ANTI_ALIAS_FLAG);
            }

            // message
            {
                ViewGroup.MarginLayoutParams margin = (ViewGroup.MarginLayoutParams) msgView.getLayoutParams();
                margin.leftMargin = (int) Display.getDimension(context, R.dimen.size_16);
                margin.rightMargin = (int) Display.getDimension(context, R.dimen.size_16);
                margin.topMargin = (titleView == null) ? (int) Display.getDimension(context, R.dimen.size_24) : 0;
                margin.bottomMargin = 0;
                msgView.setLayoutParams(margin);
                //msgView.setHeight(Display.getDimension(context, R.dimen.size_40));
                msgView.setPaddingRelative((int) Display.getDimension(context, R.dimen.size_4), 0, (int) Display.getDimension(context, R.dimen.size_4), 0);
                msgView.setGravity(Gravity.START | Gravity.CENTER_VERTICAL);
                msgView.setTextColor(Color.BLACK);
                msgView.setText(message);
                msgView.setTextSize(TypedValue.COMPLEX_UNIT_PX, Display.getDimension(context, R.dimen.text_size_14));
                msgView.setMaxHeight((int) Display.getDimension(context, R.dimen.size_300));
                msgView.setMovementMethod(ScrollingMovementMethod.getInstance());
                msgView.setScrollBarSize((int) Display.getDimension(context, R.dimen.size_1));
                msgView.setScrollBarStyle(View.SCROLLBARS_OUTSIDE_OVERLAY);
                msgView.setScrollbarFadingEnabled(false);
                msgView.setVerticalScrollBarEnabled(true);
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
                    msgView.setVerticalScrollbarThumbDrawable(new ColorDrawable(Display.getColor(context, R.color.ultrasound_color_red)));
                }
                msgView.setVerticalScrollbarPosition(View.SCROLLBAR_POSITION_RIGHT);
            }

            Button positive = dialog.getButton(AlertDialog.BUTTON_POSITIVE);
            if (positive != null) {
                positive.setMinWidth((int) Display.getDimension(context, R.dimen.size_100));
                positive.setAllCaps(false);
                positive.setTextColor(Color.parseColor("#ff1476bd"));
                positive.setTextSize(TypedValue.COMPLEX_UNIT_PX, Display.getDimension(context, R.dimen.text_size_14));
            }

            Button negative = dialog.getButton(AlertDialog.BUTTON_NEGATIVE);
            if (negative != null) negative.setVisibility(View.GONE);
        }
    }

    /**
     * 弹出式字符串输入对话框
     */
    public static class DialogInput {

        // 字符串输入对话框结果监控
        public interface OnInputListener {
            void onClick(boolean confirmButton, String input);
        }

        public interface OnEditTextListener {
            void onPreset(EditText editText);
        }

        public static void show(Context context,
                                int textResource, int hintResource, int confirmResource, int cancelResource,
                                boolean showSoftKeyboard, boolean darkBackground, boolean fullScreen,
                                OnInputListener inputListener, OnEditTextListener editTextListener) {
            DialogInput.show(context,
                    Display.getString(context, textResource),
                    Display.getString(context, hintResource),
                    Display.getString(context, confirmResource),
                    Display.getString(context, cancelResource),
                    showSoftKeyboard, darkBackground, fullScreen, inputListener, editTextListener);
        }

        public static void show(Context context,
                                String text, int hintResource, int confirmResource, int cancelResource,
                                boolean showSoftKeyboard, boolean darkBackground, boolean fullScreen,
                                OnInputListener inputListener, OnEditTextListener editTextListener) {
            DialogInput.show(context,
                    text,
                    Display.getString(context, hintResource),
                    Display.getString(context, confirmResource),
                    Display.getString(context, cancelResource),
                    showSoftKeyboard, darkBackground, fullScreen, inputListener, editTextListener);
        }

        public static void show(Context context,
                                int text, String hint, int confirmResource, int cancelResource,
                                boolean showSoftKeyboard, boolean darkBackground, boolean fullScreen,
                                OnInputListener inputListener, OnEditTextListener editTextListener) {
            DialogInput.show(context,
                    Display.getString(context, text),
                    hint,
                    Display.getString(context, confirmResource),
                    Display.getString(context, cancelResource),
                    showSoftKeyboard, darkBackground, fullScreen, inputListener, editTextListener);
        }

        public static void show(Context context,
                                int textResource, int hintResource, String confirm, String cancel,
                                boolean showSoftKeyboard, boolean darkBackground, boolean fullScreen,
                                OnInputListener inputListener, OnEditTextListener editTextListener) {
            DialogInput.show(context,
                    Display.getString(context, textResource),
                    Display.getString(context, hintResource),
                    confirm, cancel,
                    showSoftKeyboard, darkBackground, fullScreen, inputListener, editTextListener);
        }

        public static void show(Context context,
                                String text, int hintResource, String confirm, String cancel,
                                boolean showSoftKeyboard, boolean darkBackground, boolean fullScreen,
                                OnInputListener inputListener, OnEditTextListener editTextListener) {
            DialogInput.show(context,
                    text,
                    Display.getString(context, hintResource),
                    confirm, cancel,
                    showSoftKeyboard, darkBackground, fullScreen, inputListener, editTextListener);
        }

        public static void show(Context context,
                                int textResource, String hint, String confirm, String cancel,
                                boolean showSoftKeyboard, boolean darkBackground, boolean fullScreen,
                                OnInputListener inputListener, OnEditTextListener editTextListener) {
            DialogInput.show(context,
                    Display.getString(context, textResource),
                    hint, confirm, cancel,
                    showSoftKeyboard, darkBackground, fullScreen, inputListener, editTextListener);
        }

        public static void show(Context context,
                                String text, String hint, String confirm, String cancel,
                                boolean showSoftKeyboard, boolean darkBackground, boolean fullScreen,
                                OnInputListener inputListener, OnEditTextListener editTextListener) {
            EditText input = new EditText(context);
            input.setAllCaps(false);
            input.setSingleLine();
            input.setInputType(EditorInfo.TYPE_CLASS_TEXT);
            input.setImeOptions(EditorInfo.IME_ACTION_DONE | EditorInfo.IME_FLAG_NO_EXTRACT_UI);
            input.setText(text);
            input.setSelectAllOnFocus(true);
            if (editTextListener != null) {
                editTextListener.onPreset(input);
            }

            if (showSoftKeyboard) {
                input.setFocusable(true);
                input.setFocusableInTouchMode(true);
                input.requestFocus();

                new Timer().schedule(new TimerTask() {
                    @Override
                    public void run() {
                        InputMethodManager manager = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
                        if (manager != null) manager.showSoftInput(input, 0);
                    }
                }, 250);
            }

            AlertDialog dialog = new AlertDialog.Builder(context)
                    .setView(input)
                    .setPositiveButton(confirm, (dialog1, which) -> {
                        if (inputListener != null)
                            inputListener.onClick(true, input.getText().toString());
                    }).setNegativeButton(cancel, (dialog1, which) -> {
                        if (inputListener != null)
                            inputListener.onClick(false, input.getText().toString());
                    }).setCancelable(false)
                    .create();

            // 显示对话框
            Window window = dialog.getWindow();
            if (window == null) {
                dialog.show();
            } else {
                if (!darkBackground) {
                    window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
                }
                Display.setNavigationBarAndStatusBar(dialog, fullScreen);
            }

            // input
            {
                ViewGroup.MarginLayoutParams margin = (ViewGroup.MarginLayoutParams) input.getLayoutParams();
                margin.leftMargin = (int) Display.getDimension(context, R.dimen.size_16);
                margin.rightMargin = (int) Display.getDimension(context, R.dimen.size_16);
                margin.topMargin = (int) Display.getDimension(context, R.dimen.size_16);
                margin.bottomMargin = 0;
                input.setLayoutParams(margin);
                input.setHeight((int) Display.getDimension(context, R.dimen.size_40));
                input.setPaddingRelative((int) Display.getDimension(context, R.dimen.size_4), 0, (int) Display.getDimension(context, R.dimen.size_4), 0);
                input.setBackgroundResource(R.color.ultrasound_color_white);
                input.setTextSize(TypedValue.COMPLEX_UNIT_PX, Display.getDimension(context, R.dimen.text_size_14));
                input.setHint(hint);
                input.setFocusable(true);
            }

            Button positive = dialog.getButton(AlertDialog.BUTTON_POSITIVE);
            if (positive != null) {
                positive.setMinWidth((int) Display.getDimension(context, R.dimen.size_100));
                positive.setPadding((int) Display.getDimension(context, R.dimen.size_16), 0, (int) Display.getDimension(context, R.dimen.size_16), 0);
                positive.setAllCaps(false);
                positive.setTextColor(Color.parseColor("#ff1476bd"));
                positive.setTextSize(TypedValue.COMPLEX_UNIT_PX, Display.getDimension(context, R.dimen.text_size_14));
            }

            Button negative = dialog.getButton(AlertDialog.BUTTON_NEGATIVE);
            if (negative != null) {
                negative.setMinWidth((int) Display.getDimension(context, R.dimen.size_100));
                negative.setPadding((int) Display.getDimension(context, R.dimen.size_16), 0, (int) Display.getDimension(context, R.dimen.size_16), 0);
                negative.setAllCaps(false);
                negative.setTextColor(Color.BLACK);
                negative.setTextSize(TypedValue.COMPLEX_UNIT_PX, Display.getDimension(context, R.dimen.text_size_14));
            }
        }

        private static ViewTreeObserver.OnGlobalLayoutListener getGlobalLayoutListener(View decorView, View contentView) {
            return () -> {
                Rect rect = new Rect();
                decorView.getWindowVisibleDisplayFrame(rect);

                int diff = decorView.getContext().getResources().getDisplayMetrics().heightPixels - rect.bottom;

                if (diff != 0) {
                    if (contentView.getPaddingBottom() != diff) {
                        contentView.setPadding(0, 0, 0, diff);
                    }
                } else {
                    if (contentView.getPaddingBottom() != 0) {
                        contentView.setPadding(0, 0, 0, 0);
                    }
                }
            };
        }
    }

    /**
     * 弹出式确认对话框
     */
    public static class DialogConfirm {

        public interface OnClickListener {
            void onClick0();

            void onClick1();

            void onClick2();
        }

        public static void show(Context context,
                                int messageResource,
                                int buttonResource0, int buttonResource1, int buttonResource2,
                                boolean cancelable, boolean darkBackground, boolean fullScreen,
                                OnClickListener listener) {
            DialogConfirm.show(context,
                    Display.getString(context, messageResource),
                    Display.getString(context, buttonResource0),
                    Display.getString(context, buttonResource1),
                    Display.getString(context, buttonResource2),
                    cancelable, darkBackground, fullScreen,
                    listener);
        }

        public static void show(Context context,
                                String message,
                                String button0, String button1, String button2,
                                boolean cancelable, boolean darkBackground, boolean fullScreen,
                                OnClickListener listener) {
            TextView msgView = new TextView(context);

            AlertDialog dialog = new AlertDialog.Builder(context)
                    .setView(msgView)
                    .setPositiveButton(button0, (dialogInterface, i) -> {
                        if (listener != null) listener.onClick0();
                    })
                    .setNegativeButton(button1, (dialogInterface, i) -> {
                        if (listener != null) listener.onClick1();
                    })
                    .setNeutralButton(button2, (dialogInterface, i) -> {
                        if (listener != null) listener.onClick2();
                    })
                    .setCancelable(cancelable)
                    .create();

            // 显示对话框
            Window window = dialog.getWindow();
            if (window == null) {
                dialog.show();
            } else {
                if (!darkBackground) {
                    window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
                }
                Display.setNavigationBarAndStatusBar(dialog, fullScreen);
            }

            // message
            {
                ViewGroup.MarginLayoutParams margin = (ViewGroup.MarginLayoutParams) msgView.getLayoutParams();
                margin.leftMargin = (int) Display.getDimension(context, R.dimen.size_16);
                margin.rightMargin = (int) Display.getDimension(context, R.dimen.size_16);
                margin.topMargin = (int) Display.getDimension(context, R.dimen.size_24);
                margin.bottomMargin = 0;
                msgView.setLayoutParams(margin);
                msgView.setPaddingRelative((int) Display.getDimension(context, R.dimen.size_4), 0, (int) Display.getDimension(context, R.dimen.size_4), 0);
                msgView.setGravity(Gravity.START | Gravity.CENTER_VERTICAL);
                msgView.setTextColor(Color.BLACK);
                msgView.setText(message);
                msgView.setTextSize(TypedValue.COMPLEX_UNIT_PX, Display.getDimension(context, R.dimen.text_size_14));
            }

            Button positiveButton = dialog.getButton(AlertDialog.BUTTON_POSITIVE);
            Button negativeButton = dialog.getButton(AlertDialog.BUTTON_NEGATIVE);
            Button neutralButton = dialog.getButton(AlertDialog.BUTTON_NEUTRAL);

            if (positiveButton != null) {
                positiveButton.setVisibility(Str.isEmpty(button0) ? View.GONE : View.VISIBLE);
                positiveButton.setMinWidth((int) Display.getDimension(context, R.dimen.size_100));
                positiveButton.setAllCaps(false);
                positiveButton.setTextColor(Color.parseColor("#ff1476bd"));
                positiveButton.setTextSize(TypedValue.COMPLEX_UNIT_PX, Display.getDimension(context, R.dimen.text_size_14));
            }

            if (negativeButton != null) {
                negativeButton.setVisibility(Str.isEmpty(button1) ? View.GONE : View.VISIBLE);
                negativeButton.setMinWidth((int) Display.getDimension(context, R.dimen.size_100));
                negativeButton.setAllCaps(false);
                negativeButton.setTextColor((neutralButton != null && !Str.isEmpty(button2)) ? Color.parseColor("#ff1476bd") : Color.BLACK);
                negativeButton.setTextSize(TypedValue.COMPLEX_UNIT_PX, Display.getDimension(context, R.dimen.text_size_14));
            }

            if (neutralButton != null) {
                neutralButton.setVisibility(Str.isEmpty(button2) ? View.GONE : View.VISIBLE);
                neutralButton.setMinWidth((int) Display.getDimension(context, R.dimen.size_100));
                neutralButton.setAllCaps(false);
                neutralButton.setTextColor(Color.BLACK);
                neutralButton.setTextSize(TypedValue.COMPLEX_UNIT_PX, Display.getDimension(context, R.dimen.text_size_14));
            }
        }
    }

    /**
     * 弹出式进度对话框
     */
    public static class DialogProgress {

        private static ProgressDialog m_dialog;

        public interface OnCancelListener {
            void onCancel();
        }

        /**
         * 显示弹出式进度对话框(progressMax=0: 百分比模式)
         */
        public static void show(Context context, boolean loop, int messageResource, int cancelButton, boolean darkBackground, int progressCount, boolean fullScreen, OnCancelListener listener) {
            DialogProgress.show(context, loop, Display.getString(context, messageResource), Display.getString(context, cancelButton), darkBackground, progressCount, fullScreen, listener);
        }

        /**
         * 显示弹出式进度对话框(progressMax=0: 百分比模式)
         */
        public static void show(Context context, boolean loop, CharSequence message, CharSequence cancelButton, boolean darkBackground, int progressCount, boolean fullScreen, OnCancelListener listener) {
            DialogProgress.hide();
            m_dialog = new ProgressDialog(context, R.style.ThemeProgressDialogStyle);
            if (!Str.isEmpty(cancelButton.toString())) {
                m_dialog.setButton(DialogInterface.BUTTON_POSITIVE, cancelButton, new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialogInterface, int i) {
                        if (listener != null) listener.onCancel();
                    }
                });
            }
            m_dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
            if (progressCount > 0) {
                m_dialog.setProgressNumberFormat("%d/%d");
                m_dialog.setMax(progressCount);
            }
            m_dialog.setMessage(message);
            m_dialog.setIndeterminate(loop);
            if (loop) {
                m_dialog.setProgressNumberFormat(null);
                m_dialog.setProgressPercentFormat(null);
            }
            m_dialog.setCancelable(false);
            m_dialog.setCanceledOnTouchOutside(false);
            m_dialog.onStart();

            // 显示对话框
            Window window = m_dialog.getWindow();
            if (window == null) {
                m_dialog.show();
            } else {
                if (!darkBackground) {
                    window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
                }
                Display.setNavigationBarAndStatusBar(m_dialog, fullScreen);
            }

            // 修改字体
            DialogProgress.setTextFont(context, m_dialog.getWindow().getDecorView());
        }

        /**
         * 设置圆形进度条样式
         */
        public static void setIndeterminateDrawable(Context context, int idResource) {
            m_dialog.setIndeterminateDrawable(Display.getDrawable(context, idResource));
        }

        /**
         * 设置文本
         */
        public static void setMessage(int message) {
            setMessage(Display.getString(m_dialog.getContext(), message));
        }

        /**
         * 设置文本
         */
        public static void setMessage(String message) {
            if (m_dialog != null) {
                m_dialog.setMessage(message);
            }
        }

        /**
         * 设置弹出式进度对话框的进度最大值
         */
        public static void setProgressMax(int max) {
            if (m_dialog != null) {
                m_dialog.setProgressNumberFormat("%d/%d");
                m_dialog.setMax(max);
            }
        }

        /**
         * 设置弹出式进度对话框的进度值
         */
        public static void setProgress(int progress) {
            if (m_dialog != null) {
                m_dialog.setProgress(progress);
            }
        }

        /**
         * 增加弹出式进度对话框的进度值
         */
        public static void addProgress() {
            if (m_dialog != null) {
                m_dialog.setProgress(m_dialog.getProgress() + 1);
            }
        }

        /**
         * 关闭弹出式进度对话框
         */
        public static void hide() {
            if (m_dialog != null) {
                m_dialog.cancel();
                m_dialog = null;
            }
        }

        private static void setTextFont(Context context, View view) {
            if (view instanceof ViewGroup) {
                ViewGroup group = (ViewGroup) view;
                int count = group.getChildCount();
                for (int n = 0; n < count; ++n) {
                    DialogProgress.setTextFont(context, group.getChildAt(n));
                }
            } else if (view instanceof TextView) {
                ((TextView) view).setTextSize(TypedValue.COMPLEX_UNIT_PX, Display.getDimension(context, R.dimen.text_size_14));
            }
        }
    }

    /**
     * 实现对View点击事件的快速双击限制
     */
    public static class ClickProxy implements View.OnClickListener {

        private final View.OnClickListener m_listener;
        private final int m_delay;
        private long m_lastClickTime = 0;

        public ClickProxy(View.OnClickListener listener) {
            m_listener = listener;
            m_delay = 750;
        }

        public ClickProxy(View.OnClickListener listener, int delay) {
            m_listener = listener;
            m_delay = delay;
        }

        @Override
        public void onClick(View v) {
            long currentTime = Calendar.getInstance().getTimeInMillis();
            if (currentTime - m_lastClickTime > m_delay) {
                if (m_listener != null) m_listener.onClick(v);
                m_lastClickTime = currentTime;
            }
        }
    }

    /**
     * 实现对AdapterView点击事件的快速双击限制
     */
    public static class ItemClickProxy implements AdapterView.OnItemClickListener {

        private final AdapterView.OnItemClickListener m_listener;
        private final int m_delay;
        private long m_lastClickTime = 0;

        public ItemClickProxy(AdapterView.OnItemClickListener listener, int delay) {
            m_listener = listener;
            m_delay = delay;
        }

        @Override
        public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
            long currentTime = Calendar.getInstance().getTimeInMillis();
            if (currentTime - m_lastClickTime > m_delay) {
                if (m_listener != null) m_listener.onItemClick(parent, view, position, id);
                m_lastClickTime = currentTime;
            }
        }
    }

    /**
     * 小型异步任务封装类
     */
    public interface TaskPreExecute<Params> {
        void onPreExecute(Params... params);
    }

    public interface TaskDoInBackground<Params, Progress, Result> {
        Result doInBackground(TaskPublishProgress<Progress> publishProgress, Params... params);
    }

    public interface TaskProgressUpdate<Progress> {
        void onProgressUpdate(Progress... values);
    }

    public interface TaskPostExecute<Result, Params> {
        void onPostExecute(Result result, Params... params);
    }

    public interface TaskIsViewActive {
        boolean isViewActive();
    }

    public interface TaskPublishProgress<Progress> {
        void showProgress(Progress... values);
    }

    public static class Task<Params, Progress, Result> extends AsyncTask<Params, Progress, Result> implements TaskPublishProgress<Progress> {
        private Params[] m_params;
        private TaskPreExecute<Params> mPreExecute;
        private TaskProgressUpdate<Progress> mProgressUpdate;
        private TaskDoInBackground<Params, Progress, Result> mDoInBackground;
        private TaskIsViewActive mViewActive;
        private TaskPostExecute<Result, Params> mPostExecute;

        private Task() {
        }

        @Override
        protected void onPreExecute() {
            super.onPreExecute();
            if (mPreExecute != null) mPreExecute.onPreExecute(m_params);
        }

        @SafeVarargs
        @Override
        protected final void onProgressUpdate(Progress... values) {
            super.onProgressUpdate(values);
            if (mProgressUpdate != null) mProgressUpdate.onProgressUpdate(values);
        }

        @Override
        public Result doInBackground(Params... params) {
            return mDoInBackground == null ? null : mDoInBackground.doInBackground(this, params);
        }

        @Override
        protected void onPostExecute(Result result) {
            super.onPostExecute(result);
            if (mPostExecute != null && (mViewActive == null || mViewActive.isViewActive()))
                mPostExecute.onPostExecute(result, m_params);
        }

        @SafeVarargs
        public final AsyncTask<Params, Progress, Result> start(int threads, Params... params) {
            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
                return super.execute(params);
            } else {
                return super.executeOnExecutor(Executors.newFixedThreadPool(Math.max(1, threads)), params);
                //super.executeOnExecutor(THREAD_POOL_EXECUTOR, m_params);
                //super.executeOnExecutor(Executors.newSingleThreadExecutor(), m_params);
                //super.executeOnExecutor(Executors.newCachedThreadPool(), m_params);
            }
        }

        @Override
        public void showProgress(Progress[] values) {
            this.publishProgress(values);
        }

        public static <Params, Progress, Result> Builder<Params, Progress, Result> makeBuilder() {
            return new Builder<>();
        }

        public static class Builder<Params, Progress, Result> {

            private final Task<Params, Progress, Result> mAsyncTask;

            public Builder() {
                mAsyncTask = new Task<>();
            }

            public Builder<Params, Progress, Result> setReady(TaskPreExecute<Params> preExecute) {
                mAsyncTask.mPreExecute = preExecute;
                return this;
            }

            public Builder<Params, Progress, Result> setWorking(TaskDoInBackground<Params, Progress, Result> doInBackground) {
                mAsyncTask.mDoInBackground = doInBackground;
                return this;
            }

            public Builder<Params, Progress, Result> setProgress(TaskProgressUpdate<Progress> progressUpdate) {
                mAsyncTask.mProgressUpdate = progressUpdate;
                return this;
            }

            public Builder<Params, Progress, Result> setViewActive(TaskIsViewActive viewActive) {
                mAsyncTask.mViewActive = viewActive;
                return this;
            }

            public Builder<Params, Progress, Result> setFinished(TaskPostExecute<Result, Params> postExecute) {
                mAsyncTask.mPostExecute = postExecute;
                return this;
            }

            @SafeVarargs
            public final AsyncTask<Params, Progress, Result> start(int threads, Params... params) {
                mAsyncTask.m_params = params;
                return mAsyncTask.start(threads, params);
            }
        }
    }

    /**
     * 异步线程封装类
     */
    public static class Thread<Params, Result> extends java.lang.Thread {

        private OnThreadListener m_listener;
        private Params[] m_params;
        private Result m_result;
        private java.lang.Thread m_thread;
        private volatile boolean m_cancel;

        public Thread(OnThreadListener listener, Params... params) {
            m_listener = listener;
            m_params = params;
            m_thread = new java.lang.Thread(() -> {
                if (m_listener != null) {
                    m_result = (Result) m_listener.doWorking(m_params);
                }
                m_handler.sendEmptyMessage(0);
            });
        }

        /**
         * 启动异步线程
         */
        public Thread<Params, Result> execute() {
            if (m_listener != null) m_listener.doReady(m_params);
            m_cancel = false;
            m_thread.start();
            return this;
        }

        /**
         * 获取异步线程是否已经取消
         */
        public boolean isCancel() {
            return m_cancel;
        }

        /**
         * 设置运行的标识
         */
        public void setCancel() {
            m_cancel = true;
        }

        /**
         * 取消正在执行的任务
         */
        public void cancel() {
            if (m_thread != null) {
                try {
                    m_cancel = true;
                    m_thread.join();
                    m_handler.removeCallbacksAndMessages(null);
                    m_thread = null;
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }

        private final Handler m_handler = new Handler(msg -> {
            if (m_listener != null) m_listener.doFinished(m_result, m_params);
            return true;
        });

        public interface OnThreadListener<Params, Result> {
            void doReady(Params... params);

            Result doWorking(Params... params);

            void doFinished(Result result, Params... params);
        }
    }

    /**
     * 用于从文件(图像/视频/PDF)中获取图像
     */
    public static class Image {

        // 视频外部加载
        public interface OnLoadMediaListener {
            Bitmap onLoad(String file);
        }

        /**
         * 对图像的格式进行包装
         */
        public static class Container {
            public static final int Loading = -1;
            public static final int Failed = 0;
            public static final int Bitmap = 1;
            public static final int Movie = 2;
            public static final int Pdf = 3;

            public Bitmap bitmap = null;
            public int type = Loading;

            int getByteCount() {
                return (bitmap == null) ? (4) : (bitmap.getByteCount() + 4);
            }
        }

        /**
         * 获取指定宽度的缩略图
         */
        public static Bitmap scaleWidth(Bitmap bitmap, int width) {
            Bitmap ok = bitmap;
            if (width > 0) {
                if (bitmap != null) {
                    ok = Bitmap.createScaledBitmap(bitmap, width, bitmap.getHeight() * width / bitmap.getWidth(), true);
                    if (ok != bitmap) bitmap.recycle();
                    //ok = Image.scale(bitmap, (float) width / bitmap.getWidth());
                }
            }
            return ok;
        }

        /**
         * 从文件(图像/视频/PDF)中获取图像
         */
        public static Container toLoadBitmap(Context context, String file, int dwThumb, boolean accurate, OnLoadMediaListener listener) {
            return toLoadBitmap(context, new File(file), dwThumb, accurate, listener);
        }

        /**
         * 从文件(图像/视频/PDF)中获取图像
         */
        public static Container toLoadBitmap(Context context, File file, int dwThumb, boolean accurate, OnLoadMediaListener listener) {
            Container ok = new Container();
            if (file.exists()) {
                while (true) {
                    ok.bitmap = Picture.toBitmap(file, dwThumb, accurate);
                    if (ok.bitmap != null) {
                        ok.type = Container.Bitmap;
                        break;
                    }

                    ok.bitmap = Movie.toBitmap(file, dwThumb, listener);
                    if (ok.bitmap != null) {
                        ok.type = Container.Movie;
                        break;
                    }

                    ok.bitmap = Pdf.toBitmap(context, file, 0, dwThumb);
                    if (ok.bitmap != null) {
                        ok.type = Container.Pdf;
                        break;
                    }

                    ok.type = Container.Failed;
                    break;
                }
            }
            return ok;
        }

        /**
         * 用于获取图片文件的图像
         */
        public static class Picture {

            /**
             * 获取获取图片文件的图像
             */
            public static Bitmap toBitmap(String file, int dwThumb, boolean accurate) {
                return toBitmap(new File(file), dwThumb, accurate);
            }

            /**
             * 获取获取图片文件的图像
             */
            public static Bitmap toBitmap(File file, int dwThumb, boolean accurate) {
                Bitmap ok = null;
                if (file.exists()) {
                    if (dwThumb <= 0) {
                        ok = BitmapFactory.decodeFile(file.getPath());
                    } else {
                        // 读取图片
                        BitmapFactory.Options options = new BitmapFactory.Options();
                        options.inJustDecodeBounds = true;
                        BitmapFactory.decodeFile(file.getPath(), options);

                        if ((options.outWidth > 0) && (options.outHeight > 0)) {
                            int dhThumb = options.outHeight * dwThumb / options.outWidth;

                            // 计算预缩放宽高
                            options.inSampleSize = calculateInSampleSize(options, dwThumb, dhThumb);

                            // 预缩放
                            options.inJustDecodeBounds = false;
                            Bitmap src = ok = BitmapFactory.decodeFile(file.getPath(), options);

                            // 精确缩放
                            if (accurate && (src != null)) {
                                ok = Bitmap.createScaledBitmap(src, dwThumb, dhThumb, true);
                                if (ok != src) src.recycle();
                            }
                        }
                    }
                }
                return ok;
            }
        }

        /**
         * 用于获取视频的第一帧图像
         */
        public static class Movie {

            /**
             * 获取视频的第一帧图像
             */
            public static Bitmap toBitmap(String file, int dwThumb, OnLoadMediaListener listener) {
                return toBitmap(new File(file), dwThumb, listener);
            }

            /**
             * 获取视频的第一帧图像
             */
            public static Bitmap toBitmap(File file, int dwThumb, OnLoadMediaListener listener) {
                Bitmap ok = null;
                if (file.exists()) {
                    MediaMetadataRetriever media = null;
                    try {
                        media = new MediaMetadataRetriever();
                        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH)
                            media.setDataSource(file.getPath(), new HashMap<String, String>());
                        else
                            media.setDataSource(file.getPath());
                        ok = Image.scaleWidth(media.getFrameAtTime(), dwThumb);
                    } catch (Exception e) {
                        e.printStackTrace();
                    } finally {
                        if (media != null) {
                            try {
                                media.release();
                            } catch (IOException e) {
                                throw new RuntimeException(e);
                            }
                        }
                    }
                    if ((ok == null) && (listener != null)) {
                        ok = Image.scaleWidth(listener.onLoad(file.getPath()), dwThumb);
                    }
                }
                return ok;
            }
        }

        /**
         * 用于将PDF的第一页转换为Bitmap
         */
        public static class Pdf {
            /**
             * 获取PDF文件中共有几页
             */
            public static int getPageCount(String file) {
                return getPageCount(new File(file));
            }

            /**
             * 获取PDF文件中共有几页
             */
            public static int getPageCount(File file) {
                int ok = 0;
                try {
                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                        PdfRenderer renderer = new PdfRenderer(ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY));
                        ok = renderer.getPageCount();
                        renderer.close();
                    }
                } catch (Exception exception) {
                    exception.printStackTrace();
                }
                return ok;
            }

            /**
             * 获取将PDF指定页转换为Bitmap
             */
            public static Bitmap toBitmap(Context context, String file, int page, int dwThumb) {
                return toBitmap(context, new File(file), page, dwThumb);
            }

            /**
             * 获取将PDF指定页转换为Bitmap
             */
            public static Bitmap toBitmap(Context context, File file, int page, int dwThumb) {
                Bitmap ok = null;
                try {
                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                        ParcelFileDescriptor fileDescriptor = ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY);
                        if (fileDescriptor != null) {
                            PdfRenderer renderer = new PdfRenderer(fileDescriptor);
                            int pageCount = renderer.getPageCount();
                            if (pageCount > 0) {
                                PdfRenderer.Page pageRenderer = renderer.openPage(page);
                                if (pageRenderer != null) {
                                    int width = context.getResources().getDisplayMetrics().densityDpi / 72 * pageRenderer.getWidth();
                                    int height = context.getResources().getDisplayMetrics().densityDpi / 72 * pageRenderer.getHeight();
                                    ok = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
                                    //todo 以下三行解决图片存储到本地出现黑屏的问题
                                    Canvas canvas = new Canvas(ok);
                                    canvas.drawColor(Color.WHITE);
                                    canvas.drawBitmap(ok, 0, 0, new Paint(Paint.ANTI_ALIAS_FLAG));
                                    Rect r = new Rect(0, 0, width, height);
                                    pageRenderer.render(ok, r, null, PdfRenderer.Page.RENDER_MODE_FOR_DISPLAY);
                                    pageRenderer.close();
                                }
                                if (ok != null) ok = Image.scaleWidth(ok, dwThumb);
                            }
                            renderer.close();
                        }
                        fileDescriptor.close();
                    }
                } catch (Exception exception) {
                    exception.printStackTrace();
                }
                return ok;
            }

            /**
             * 获取将PDF所有页转换为Bitmap数组
             */
            public static ArrayList<Bitmap> toBitmapArray(Context context, String file) {
                return toBitmapArray(context, new File(file));
            }

            /**
             * 获取将PDF所有页转换为Bitmap数组
             */
            public static ArrayList<Bitmap> toBitmapArray(Context context, File file) {
                ArrayList<Bitmap> ok = new ArrayList<>();
                try {
                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                        ParcelFileDescriptor fileDescriptor = ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY);
                        if (fileDescriptor != null) {
                            PdfRenderer renderer = new PdfRenderer(fileDescriptor);
                            Bitmap bitmap;
                            int pageCount = renderer.getPageCount();
                            for (int n = 0; n < pageCount; ++n) {
                                PdfRenderer.Page pageRenderer = renderer.openPage(n);
                                if (pageRenderer != null) {
                                    int width = context.getResources().getDisplayMetrics().densityDpi / 72 * pageRenderer.getWidth();
                                    int height = context.getResources().getDisplayMetrics().densityDpi / 72 * pageRenderer.getHeight();
                                    bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
                                    //todo 以下三行解决图片存储到本地出现黑屏的问题
                                    Canvas canvas = new Canvas(bitmap);
                                    canvas.drawColor(Color.WHITE);
                                    canvas.drawBitmap(bitmap, 0, 0, new Paint(Paint.ANTI_ALIAS_FLAG));
                                    Rect r = new Rect(0, 0, width, height);
                                    pageRenderer.render(bitmap, r, null, PdfRenderer.Page.RENDER_MODE_FOR_DISPLAY);
                                    ok.add(bitmap);
                                    pageRenderer.close();
                                }
                            }
                            renderer.close();
                        }
                        fileDescriptor.close();
                    }
                } catch (Exception exception) {
                    exception.printStackTrace();
                }
                return ok;
            }
        }

        private static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
            int inSampleSize = 1;
            {
                int width = options.outWidth;
                int height = options.outHeight;

                if ((width > reqWidth) || (height > reqHeight)) {
                    int halfHeight = height / 2;
                    int halfWidth = width / 2;
                    while ((halfHeight / inSampleSize) > reqHeight && (halfWidth / inSampleSize) > reqWidth) {
                        inSampleSize *= 2;
                    }
                }
            }
            return inSampleSize;
        }
    }

    /**
     * 对Toast的封装
     */
    public static class Toast extends android.widget.Toast {

        public static final int LENGTH_SHORT = android.widget.Toast.LENGTH_SHORT;
        public static final int LENGTH_LONG = android.widget.Toast.LENGTH_LONG;

        private static Toast m_toast = null;

        private Toast(Context context) {
            super(context);
        }

        /**
         * 纯文本提示框
         */
        public static synchronized void text(Context context, String text, int duration) {
            show("text", context, text, duration);
        }

        public static synchronized void text(Context context, @StringRes int idText, int duration) {
            show("text", context, context.getResources().getText(idText), duration);
        }

        /**
         * 带图标的提示框 - 原生背景
         */
        public static synchronized void common(Context context, String text, int duration) {
            show("common", context, text, duration);
        }

        public static synchronized void common(Context context, @StringRes int idText, int duration) {
            show("common", context, context.getResources().getText(idText), duration);
        }

        /**
         * 带图标的提示框 - 成功
         */
        public static synchronized void success(Context context, String text, int duration) {
            show("success", context, text, duration);
        }

        public static synchronized void success(Context context, @StringRes int idText, int duration) {
            show("success", context, context.getResources().getText(idText), duration);
        }

        /**
         * 带图标的提示框 - 失败
         */
        public static synchronized void failed(Context context, String text, int duration) {
            show("failed", context, text, duration);
        }

        public static synchronized void failed(Context context, @StringRes int idText, int duration) {
            show("failed", context, context.getResources().getText(idText), duration);
        }

        /**
         * 带图标的提示框 - 警告
         */
        public static synchronized void warning(Context context, String text, int duration) {
            show("warning", context, text, duration);
        }

        public static synchronized void warning(Context context, @StringRes int idText, int duration) {
            show("warning", context, context.getResources().getText(idText), duration);
        }

        /**
         * 带图标的提示框 - 错误
         */
        public static synchronized void error(Context context, String text, int duration) {
            show("error", context, text, duration);
        }

        public static synchronized void error(Context context, @StringRes int idText, int duration) {
            show("error", context, context.getResources().getText(idText), duration);
        }

        /**
         * 带图标的提示框 - info
         */
        public static synchronized void info(Context context, String text, int duration) {
            show("info", context, text, duration);
        }

        public static synchronized void info(Context context, @StringRes int idText, int duration) {
            show("info", context, context.getResources().getText(idText), duration);
        }

        /**
         * 处理显示toast的具体逻辑
         */
        private static void show(String msgType, Context context, CharSequence text, int duration) {
            View view = LayoutInflater.from(context).inflate(R.layout.utils_toast_layout, null, false);
            ImageView imageView = view.findViewById(R.id.toast_icon);
            TextView textView = view.findViewById(R.id.toast_text);

            // 判断toast类型
            if ("text".equals(msgType)) {
                imageView.setVisibility(View.GONE);
            } else if ("common".equals(msgType)) {
                Display.recycle(imageView, true);
                imageView.setImageResource(R.drawable.helper_toast_info);
            } else if ("success".equals(msgType)) {
                Display.recycle(imageView, true);
                imageView.setImageResource(R.drawable.helper_toast_success);
            } else if ("failed".equals(msgType)) {
                Display.recycle(imageView, true);
                imageView.setImageResource(R.drawable.helper_toast_failed);
            } else if ("warning".equals(msgType)) {
                Display.recycle(imageView, true);
                imageView.setImageResource(R.drawable.helper_toast_warning);
            } else if ("error".equals(msgType)) {
                Display.recycle(imageView, true);
                imageView.setImageResource(R.drawable.helper_toast_error);
            } else if ("info".equals(msgType)) {
                Display.recycle(imageView, true);
                imageView.setImageResource(R.drawable.helper_toast_info);
            }

            textView.setText(text);

            // 计算并设置宽度
            {
                int width = 0;
                Paint paint = new Paint();
                paint.setTextSize(textView.getTextSize());
                Rect rect = new Rect();
                paint.getTextBounds(text.toString(), 0, text.length(), rect);

                width += Math.max(view.getPaddingLeft(), view.getPaddingStart());
                width += Display.getDimension(context, R.dimen.size_36); // toast_icon
                width += Display.getDimension(context, R.dimen.size_8);  // marginLeft
                width += rect.width();
                width += Math.max(view.getPaddingRight(), view.getPaddingEnd());

                view.setMinimumWidth(Math.min(width, Display.getScreenWidth(context) * 2 / 3));
            }

            // 通过Toast.getInstance(context)方法获取单例对象
            Toast toast = getInstance(context);
            toast.setDuration(getToastDuration(duration));
            toast.setView(view);
            //toast.setGravity(Gravity.BOTTOM, 0, 50);
            toast.show();
        }

        private static Toast getInstance(Context context) {
            if (m_toast != null) {
                m_toast.cancel();
                m_toast = null;
            }
            m_toast = new Toast(context);
            return m_toast;
        }

        // 获取duration,只能选择Toast.LENGTH_SHORT或Toast.LENGTH_LONG,防止调用时传入非法参数
        private static int getToastDuration(int duration) {
            if (duration == android.widget.Toast.LENGTH_LONG) {
                return android.widget.Toast.LENGTH_LONG;
            } else {
                return android.widget.Toast.LENGTH_SHORT;
            }
        }
    }

    /**
     * 环形缓冲,解决移除数据时的缓冲数据复制的消耗
     */
    public static class CircleBuffer {
        private final int mCapacity;
        private byte[] mBuffer;
        private int mSize;
        private int mReadPos;
        private int mWritePos;

        /**
         * 分配缓存
         */
        public CircleBuffer(int capacity) {
            mCapacity = capacity;
            mBuffer = new byte[mCapacity];
            mSize = 0;
            mReadPos = 0;
            mWritePos = 0;
        }

        /**
         * 复位
         */
        public void reset() {
            mSize = 0;
            mReadPos = 0;
            mWritePos = 0;
        }

        /**
         * 获取缓冲长度
         */
        public int capacity() {
            return mCapacity;
        }

        /**
         * 获取数据长度
         */
        public int size() {
            return mSize;
        }

        /**
         * 数据是否为空
         */
        public boolean isEmpty() {
            return (mSize == 0);
        }

        /**
         * 数据是否已满
         */
        public boolean isFull() {
            return (mSize == mCapacity);
        }

        /**
         * 向缓冲区写入数据,返回实际写入的字节数---将影响写入位置
         */
        public int writeBytes(byte[] data) {
            return this.writeBytes(data, data.length);
        }

        /**
         * 向缓冲区写入数据,返回实际写入的字节数---将影响写入位置
         */
        public int writeBytes(byte[] data, int length) {
            int ok = 0;
            if ((data != null) && (length > 0)) {
                int bytes_to_write = Math.min(length, mCapacity - mSize);
                if (bytes_to_write > 0) {
                    if (bytes_to_write <= mCapacity - mWritePos) {
                        System.arraycopy(data, 0, mBuffer, mWritePos, bytes_to_write);
                        mWritePos += bytes_to_write;
                        if (mWritePos == mCapacity) mWritePos = 0;
                    } else {
                        int size1 = mCapacity - mWritePos;
                        int size2 = bytes_to_write - size1;
                        System.arraycopy(data, 0, mBuffer, mWritePos, size1);
                        System.arraycopy(data, size1, mBuffer, 0, size2);
                        mWritePos = size2;
                    }
                    mSize += bytes_to_write;
                    ok = bytes_to_write;
                }
            }
            return ok;
        }

        /**
         * 从缓冲区读数据,返回实际读取的字节数---将影响读取位置
         */
        public int readBytes(byte[] data) {
            int ok = 0;
            if ((data != null) && (data.length > 0)) {
                int bytes_to_read = Math.min(data.length, mSize);
                if (bytes_to_read > 0) {
                    if (bytes_to_read <= mCapacity - mReadPos) {
                        System.arraycopy(mBuffer, mReadPos, data, 0, bytes_to_read);
                        mReadPos += bytes_to_read;
                        if (mReadPos == mCapacity) mReadPos = 0;
                    } else {
                        int size1 = mCapacity - mReadPos;
                        int size2 = bytes_to_read - size1;
                        System.arraycopy(mBuffer, mReadPos, data, 0, size1);
                        System.arraycopy(mBuffer, 0, data, size1, size2);
                        mReadPos = size2;
                    }
                    mSize -= bytes_to_read;
                    ok = bytes_to_read;
                }
            }
            return ok;
        }

        /**
         * 从缓冲区读删除一段数据---将影响读取位置
         */
        public void remove(int size) {
            if (size > 0) {
                int bytes_to_read = Math.min(size, mSize);
                if (bytes_to_read > 0) {
                    if (bytes_to_read <= mCapacity - mReadPos) {
                        mReadPos += bytes_to_read;
                        if (mReadPos == mCapacity) mReadPos = 0;
                    } else {
                        mReadPos = bytes_to_read - (mCapacity - mReadPos);
                    }
                    mSize -= bytes_to_read;
                }
            }
        }

        /**
         * 从缓冲区读一个byte数据---不影响读取位置
         */
        public byte getByte(int pos) {
            byte ok = 0;
            if ((pos >= 0) && (pos + 1 <= mSize)) {
                ok = mBuffer[(mReadPos + pos) % mCapacity];
            }
            return ok;
        }

        /**
         * 从缓冲区读一个short数据---不影响读取位置
         */
        public short getShort(int pos) {
            short ok = 0;
            if ((pos >= 0) && (pos + 2 <= mSize)) {
                ok |= (mBuffer[(mReadPos + pos) % mCapacity] & 0xff) << 8;
                ok |= (mBuffer[(mReadPos + pos + 1) % mCapacity] & 0xff);
            }
            return ok;
        }

        /**
         * 从缓冲区读一个int数据---不影响读取位置
         */
        public int getInt(int pos) {
            int ok = 0;
            if ((pos >= 0) && (pos + 4 <= mSize)) {
                ok |= (mBuffer[(mReadPos + pos) % mCapacity] & 0xff) << 24;
                ok |= (mBuffer[(mReadPos + pos + 1) % mCapacity] & 0xff) << 16;
                ok |= (mBuffer[(mReadPos + pos + 2) % mCapacity] & 0xff) << 8;
                ok |= (mBuffer[(mReadPos + pos + 3) % mCapacity] & 0xff);
            }
            return ok;
        }

        /**
         * 获取指定长度的数据---不影响读取位置
         */
        public byte[] getData(int length) {
            byte[] ok = null;
            if ((length > 0) && (length <= mSize)) {
                ok = new byte[length];
                if (length <= mCapacity - mReadPos) {
                    System.arraycopy(mBuffer, mReadPos, ok, 0, length);
                } else {
                    int size1 = mCapacity - mReadPos;
                    int size2 = length - size1;
                    System.arraycopy(mBuffer, mReadPos, ok, 0, size1);
                    System.arraycopy(mBuffer, 0, ok, size1, size2);
                }
            }
            return ok;
        }
    }

    /**
     * 文本文件输出
     */
    public static class TextWriter {
        private OutputStreamWriter m_writer = null;

        /**
         * 打开文件
         */
        public TextWriter(String file, boolean append) {
            try {
                m_writer = new OutputStreamWriter(new FileOutputStream(file, append), StandardCharsets.UTF_8);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        /**
         * 写字符串到文件
         */
        public void write(String text) {
            if (m_writer != null) {
                try {
                    m_writer.write(text);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

        /**
         * 关闭文件
         */
        public void close() {
            if (m_writer != null) {
                try {
                    m_writer.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

        /**
         * 写入文件
         */
        public static void write(String file, String text, boolean append) {
            try {
                OutputStreamWriter writer = new OutputStreamWriter(new FileOutputStream(file, append), StandardCharsets.UTF_8);
                writer.write(text);
                writer.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * INI文件操作类
     */
    public static class IniFile implements Serializable {
        private File m_file = null;         // 当前文件
        private String m_separator = "\n";  // 换行符
        private String m_charSet = "UTF-8"; // 编码
        private final Map<String, Section> m_sections = new LinkedHashMap<>();

        /**
         * 点节
         */
        public static class Section {

            private String m_name;
            private final Map<String, Object> m_values = new LinkedHashMap<>();

            public String getName() {
                return m_name;
            }

            public void setName(String name) {
                this.m_name = name;
            }

            public void set(String key, Object value) {
                m_values.put(key, value);
            }

            public Object get(String key) {
                return m_values.get(key);
            }

            public Map<String, Object> getValues() {
                return m_values;
            }
        }

        /**
         * 当前操作的文件对像
         */
        public IniFile() {
        }

        public IniFile(File file) {
            m_file = file;
            this.load(file);
        }

        public IniFile(InputStream inputStream) {
            this.load(inputStream);
        }

        /**
         * 指定换行符
         */
        public void setLineSeparator(String separator) {
            m_separator = separator;
        }

        /**
         * 指定编码
         */
        public void setCharSet(String charSet) {
            m_charSet = charSet;
        }

        /**
         * 设置值
         *
         * @param section 节点
         * @param key     属性名
         * @param value   属性值
         */
        public void set(String section, String key, Object value) {
            Section object = m_sections.get(section);
            if (object == null) object = new Section();
            object.m_name = section;
            object.set(key, value);
            m_sections.put(section, object);
        }

        /**
         * 获取节点
         *
         * @param section 节点名称
         */
        public Section get(String section) {
            return m_sections.get(section);
        }

        /**
         * 获取值
         *
         * @param section 节点名称
         * @param key     属性名称
         */
        public Object get(String section, String key) {
            return this.get(section, key, null);
        }

        /**
         * 获取值
         *
         * @param section  节点名称
         * @param key      属性名称
         * @param defValue 如果为空返回默认值
         */
        public Object get(String section, String key, String defValue) {
            Object ok = null;
            {
                Section object = m_sections.get(section);
                if (object != null) {
                    ok = object.get(key);
                    if (ok == null || ok.toString().trim().equals("")) ok = defValue;
                }
            }
            return ok;
        }

        /**
         * 删除节点
         *
         * @param section 节点名称
         */
        public void remove(String section) {
            m_sections.remove(section);
        }

        /**
         * 删除属性
         *
         * @param section 节点名称
         * @param key     属性名称
         */
        public void remove(String section, String key) {
            Section object = m_sections.get(section);
            if (object != null) object.getValues().remove(key);
        }

        /**
         * 加载一个ini文件
         */
        public void load(File file) {
            m_file = file;
            try {
                this.load(new BufferedReader(new FileReader(file)));
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            }
        }

        /**
         * 加载一个输入流
         */
        public void load(InputStream inputStream) {
            try {
                this.load(new BufferedReader(new InputStreamReader(inputStream, m_charSet)));
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
        }

        /**
         * 保存到当前文件
         */
        public void save() {
            this.save(m_file);
        }

        /**
         * 保存到文件
         */
        public void save(File file) {
            try {
                this.save(new BufferedWriter(new FileWriter(file)));
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        /**
         * 写到输出流中
         */
        public void save(OutputStream outputStream) {
            try {
                this.save(new BufferedWriter(new OutputStreamWriter(outputStream, m_charSet)));
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
        }

        // 从BufferedReader初始化IniFile
        private void load(BufferedReader reader) {
            String line;
            Section section = null;
            Pattern pattern = Pattern.compile("^\\[.*\\]$");
            try {
                while ((line = reader.readLine()) != null) {
                    line = line.trim();
                    if (pattern.matcher((line)).matches()) {
                        section = new Section();
                        section.m_name = line.substring(1, line.length() - 1);
                        m_sections.put(section.m_name, section);
                    } else {
                        String[] value = line.split("=");
                        if (value.length == 2) {
                            section.set(value[0], value[1]);
                        }
                    }
                }
                reader.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        // 保存Ini文件
        private void save(BufferedWriter writer) {
            try {
                boolean separator = false;
                if (m_separator == null || m_separator.trim().equals(""))
                    separator = true;
                for (Section section : m_sections.values()) {
                    writer.write("[" + section.getName() + "]");
                    if (separator) writer.write(m_separator);
                    else writer.newLine();
                    for (Map.Entry<String, Object> entry : section.getValues().entrySet()) {
                        writer.write(entry.getKey());
                        writer.write("=");
                        writer.write(entry.getValue().toString());
                        if (separator) writer.write(m_separator);
                        else writer.newLine();
                    }
                }
                writer.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 设备信息
     */
    public static class DeviceInfo {

        /**
         * 获取制造商
         */
        public static String getManufacturer() {
            return Build.MANUFACTURER;
        }

        /**
         * 获取产品型号
         */
        public static String getProduct() {
            return Build.PRODUCT;
        }

        /**
         * 获取型号
         */
        public static String getModel() {
            return Build.MODEL;
        }

        /**
         * 获取处理器
         */
        public static String getHardware() {
            return Build.HARDWARE;
        }

        /**
         * 获取系统架构
         */
        public static String getAbi() {
            return Build.CPU_ABI;
        }

        /**
         * 获取系统版本号
         */
        public static String getAndroidVersion() {
            return "Android " + Build.VERSION.RELEASE;
        }

        /**
         * 是否为鸿蒙系统
         */
        public static boolean isHarmony() {
            boolean ok = false;
            try {
                Class<?> build = Class.forName("com.huawei.system.BuildEx");
                Object brand = build.getMethod("getOsBrand").invoke(build);
                if (brand != null) ok = brand.toString().equals("harmony");
            } catch (Throwable e) {
                e.printStackTrace();
            }
            return ok;
        }

        /**
         * 获取鸿蒙系统版本号
         */
        public static String getHarmonyVersion() {
            String ok = "";
            try {
                @SuppressLint("PrivateApi") Class<?> clz = Class.forName("android.os.SystemProperties");
                Method method = clz.getDeclaredMethod("get", String.class);
                ok = (String) method.invoke(clz, "hw_sc.build.platform.version");
            } catch (Throwable e) {
                e.printStackTrace();
            }
            return ok;
        }

        /**
         * 获取系统语言
         */
        public static String getLanguage() {
            return Locale.getDefault().getLanguage() + "_" + Locale.getDefault().getCountry();
        }

        /**
         * 获取当前系统上的语言列表(Locale列表)
         */
        public static Locale[] getSystemLanguageList() {
            return Locale.getAvailableLocales();
        }

        /**
         * 获取内存信息
         */
        public static String getRam(Context context) {
            final long[] map = {1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192};
            ActivityManager manager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
            ActivityManager.MemoryInfo info = new ActivityManager.MemoryInfo();
            manager.getMemoryInfo(info);
            for (long i : map) {
                long size = i * 1073741824;
                if (info.totalMem <= size) {
                    info.totalMem = size;
                    break;
                }
            }
            return Disk.toString(info.availMem, "#.00") + " / " + Disk.toString(info.totalMem, "#");
        }

        /**
         * 获取存储信息
         */
        public static String getStorage() {
            long total = Disk.getTotal();
            final long[] map = {1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192};
            for (long i : map) {
                long size = i * 1073741824;
                if (total <= size) {
                    total = size;
                    break;
                }
            }
            return Disk.toString(Disk.getAvailable(), "#.00") + " / " + Disk.toString(total, "#");
        }

        /**
         * 获取屏幕分辨率
         */
        public static String getResolution(Context context) {
            return Str.format("%d x %d, %.0f", Display.getScreenLogicalWidth(context), Display.getScreenLogicalHeight(context), Display.getDensity(context));
        }
    }
}